[Solved] Help with ctinfo, iptables, and CONNMARK savedscp-mark

I'm running OpenWRT from a 19.07 snapshot with three cherrypicked commits from master for savedscp-mark (1aad1d17ed8bee2a22d235980a6c80b6dc1b74e0, dba5a01358d98cf5a83826f1890f9aa31a49a4d4, and 4bc02a421fbdf2b69e856dbe2829b2685deca8c1).

I'm using a slightly modified version of the sqm-scripts my_layer_cake.qos that @ldir had linked in another thread. The only notable changes are that the ctinfo arguments needed changed ("0xfc000000 0x01000000" instead of "0xfc000000/0x01000000") and the contents of the QOS_MARK_${IFACE} chain:

#!/bin/sh
# Cero3 Shaper
# A cake shaper and AQM solution that allows several diffserv marking schemes
# for ethernet gateways

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
#       Copyright (C) 2012-5 Michael D. Taht, Toke Høiland-Jørgensen, Sebastian Moeller


#sm: TODO pass in the cake diffserv keyword

. ${SQM_LIB_DIR}/defaults.sh
QDISC=cake

# Default traffic classication is passed in INGRESS_CAKE_OPTS and EGRESS_CAKE_OPTS, defined in defaults.sh now


egress() {
    SILENT=1 $TC qdisc del dev $IFACE root
    $TC qdisc add dev $IFACE root handle cacf: $( get_stab_string ) cake \
        bandwidth ${UPLINK}kbit $( get_cake_lla_string ) ${EGRESS_CAKE_OPTS} ${EQDISC_OPTS}

    # put an action on the egress interface to set DSCP from the stored connmark.
    # this seems counter intuitive but it ensures once the mark is set that all
    # subsequent egress packets have the same stored DSCP avoiding iptables rules
    # to mark every packet, ctinfo does it for us and then CAKE is happy using the
    # DSCP
    $TC filter add dev $IFACE protocol all prio 10 u32 match u32 0 0 action \
	ctinfo dscp 0xfc000000 0x01000000
}


ingress() {

    SILENT=1 $TC qdisc del dev $IFACE handle ffff: ingress
    $TC qdisc add dev $IFACE handle ffff: ingress

    SILENT=1 $TC qdisc del dev $DEV root

    [ "$IGNORE_DSCP_INGRESS" -eq "1" ] && INGRESS_CAKE_OPTS="$INGRESS_CAKE_OPTS besteffort"
    [ "$ZERO_DSCP_INGRESS" -eq "1" ] && INGRESS_CAKE_OPTS="$INGRESS_CAKE_OPTS wash"

    $TC qdisc add dev $DEV root handle cace: $( get_stab_string ) cake \
        bandwidth ${DOWNLINK}kbit $( get_cake_lla_string ) ${INGRESS_CAKE_OPTS} ${IQDISC_OPTS}

    $IP link set dev $DEV up

    # redirect all IP packets arriving in $IFACE to ifb0
    # set DSCP from conntrack mark
    $TC filter add dev $IFACE parent ffff: protocol all prio 10 u32 \
	match u32 0 0 action \
	ctinfo dscp 0xfc000000 0x01000000 \
	mirred egress redirect dev $DEV

    # Configure iptables chain to mark packets
    ipt -t mangle -N QOS_MARK_${IFACE}

    # Change DSCP of relevant hosts/packets
    # and save the DSCP to the connmark using savedscp 
    
    #From sched_cake.c:
    # /*  Further pruned list of traffic classes for four-class system:
    # *
    # *	    Latency Sensitive  (CS7, CS6, EF, VA, CS5, CS4)
    # *	    Streaming Media    (AF4x, AF3x, CS3, AF2x, TOS4, CS2, TOS1)
    # *	    Best Effort        (CS0, AF1x, TOS2, and those not specified)
    # *	    Background Traffic (CS1)
    # *
    # *		Total 4 traffic classes.
    # */
    
    #and for diffserv8:
    # /*	Pruned list of traffic classes for typical applications:
    # *
    # *		Network Control          (CS6, CS7)
    # *		Minimum Latency          (EF, VA, CS5, CS4)
    # *		Interactive Shell        (CS2, TOS1)
    # *		Low Latency Transactions (AF2x, TOS4)
    # *		Video Streaming          (AF4x, AF3x, CS3)
    # *		Bog Standard             (CS0 etc.)
    # *		High Throughput          (AF1x, TOS2)
    # *		Background Traffic       (CS1)
    # *
    # *		Total 8 traffic classes.
    # */
 
#    iptables -t mangle -A QOS_MARK_${IFACE} -p tcp -s 192.168.1.2 -m comment --comment "Testing to see if ctinfo is working" -j DSCP --set-dscp-class AF41
#    iptables -t mangle -A QOS_MARK_${IFACE} -p udp -s 192.168.1.2 -m comment --comment "Testing to see if ctinfo is working" -j DSCP --set-dscp-class AF41

# Template for small packets (test)
# ipt -t mangle -A QOS_MARK_${IFACE} -p tcp -s 192.168.1.152 -m multiport --dports 80,443 -m connbytes --connbytes-dir both --connbytes-mode avgpkt --connbytes 0:1200 -m comment --comment "Elijah's Switch DSCP" -j DSCP --set-dscp-class ${GAMING_CLASS}
#   
    GAMING_CLASS=AF41 #was CS3
    ipt -t mangle -A QOS_MARK_${IFACE} -m set --match-set gameset dst -j DSCP --set-dscp-class ${GAMING_CLASS}
    ipt -t mangle -A QOS_MARK_${IFACE} -m comment --comment "Match bulk traffic" -m set --match-set bulkset dst -j DSCP --set-dscp-class CS1
    #Mike Wii U
    ipt -t mangle -A QOS_MARK_${IFACE} -p tcp -s 192.168.1.3 -m multiport --dports 1024:65535 -j DSCP --set-dscp-class ${GAMING_CLASS}
    ipt -t mangle -A QOS_MARK_${IFACE} -p udp -s 192.168.1.3 -m multiport --dports 1024:65535 -j DSCP --set-dscp-class ${GAMING_CLASS}
    #Hayden Wii U
    ipt -t mangle -A QOS_MARK_${IFACE} -p tcp -s 192.168.1.4 -m multiport --dports 1024:65535 -j DSCP --set-dscp-class ${GAMING_CLASS}
    ipt -t mangle -A QOS_MARK_${IFACE} -p udp -s 192.168.1.4 -m multiport --dports 1024:65535 -j DSCP --set-dscp-class ${GAMING_CLASS}
    #Hayden Switch
    ipt -t mangle -A QOS_MARK_${IFACE} -m comment --comment "DSCP for Hayden's Switch" -p tcp -s 192.168.1.166 -m multiport --dports 1024:65535 -j DSCP --set-dscp-class ${GAMING_CLASS}
    ipt -t mangle -A QOS_MARK_${IFACE} -p udp -s 192.168.1.166 -m multiport --dports 1024:65535 -j DSCP --set-dscp-class ${GAMING_CLASS}
    #Elijah Switch
    ipt -t mangle -A QOS_MARK_${IFACE} -p tcp -s 192.168.1.152 -m multiport --dports 1024:65535 -j DSCP --set-dscp-class ${GAMING_CLASS}
    ipt -t mangle -A QOS_MARK_${IFACE} -p udp -s 192.168.1.152 -m multiport --dports 1024:65535 -j DSCP --set-dscp-class ${GAMING_CLASS}
    
    ipt -A QOS_MARK_${IFACE} -t mangle -j CONNMARK --savedscp-mark 0xfc000000/0x01000000
    # Send unmarked connections to the marking chain
    # top 6 bits are DSCP, LSB is DSCP is valid flag
    # ipt -t mangle -A PREROUTING  -i $IFACE -m connmark --mark 0x00000000/0x01000000 -g QOS_MARK_${IFACE}
    ipt -t mangle -A POSTROUTING -o $IFACE -m connmark --mark 0x00000000/0x01000000 -g QOS_MARK_${IFACE}

    #you could just send every packet to the marking chain and update the stored DSCP for every packet
    #which should work for dynamic type marking but at a cpu cost
}

sqm_prepare_script() {
    do_modules
    verify_qdisc $QDISC "cake" || return 1
}

The filters seem to be enabled:

root@OpenWrt:~# tc -s -d filter show dev eth0
filter parent cacf: protocol all pref 10 u32 chain 0
filter parent cacf: protocol all pref 10 u32 chain 0 fh 800: ht divisor 1
filter parent cacf: protocol all pref 10 u32 chain 0 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:1 not_in_hw
  match 00000000/00000000 at 0
        action order 1: ctinfo zone 0 pipe
         index 1 ref 1 bind 1 dscp 0xfc000000 0x01000000 installed 4197 sec DSCP set 0 error 0 CPMARK set 0
        Action statistics:
        Sent 25791675 bytes 192671 pkt (dropped 0, overlimits 0 requeues 0)
        backlog 0b 0p requeues 0

root@OpenWrt:~#
root@OpenWrt:~# tc -s -d filter show dev eth0 egress
filter parent ffff: protocol all pref 10 u32 chain 0
filter parent ffff: protocol all pref 10 u32 chain 0 fh 800: ht divisor 1
filter parent ffff: protocol all pref 10 u32 chain 0 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:1 not_in_hw
  match 00000000/00000000 at 0
        action order 1: ctinfo zone 0 pipe
         index 2 ref 1 bind 1 dscp 0xfc000000 0x01000000 installed 4208 sec used 0 sec DSCP set 0 error 0 CPMARK set 0
        Action statistics:
        Sent 720870953 bytes 553321 pkt (dropped 0, overlimits 0 requeues 0)
        backlog 0b 0p requeues 0

        action order 2: mirred (Egress Redirect to device ifb4eth0) stolen
        index 1 ref 1 bind 1 installed 4208 sec used 0 sec
        Action statistics:
        Sent 720870953 bytes 553321 pkt (dropped 0, overlimits 0 requeues 0)
        backlog 0b 0p requeues 0

Notable there is that DSCP set is "0". Which leads me to where I suspect the root of the problem is:

Chain PREROUTING (policy ACCEPT 30039 packets, 20M bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain INPUT (policy ACCEPT 141 packets, 12177 bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain FORWARD (policy ACCEPT 29802 packets, 20M bytes)
 pkts bytes target     prot opt in     out     source               destination
 2880  161K TCPMSS     tcp  --  any    eth0.2  anywhere             anywhere             tcp flags:SYN,RST/SYN /* !fw3: Zone wan MTU fixing */ TCPMSS clamp to PMTU

Chain OUTPUT (policy ACCEPT 165 packets, 22561 bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain POSTROUTING (policy ACCEPT 29967 packets, 20M bytes)
 pkts bytes target     prot opt in     out     source               destination
    0     0 QOS_MARK_eth0  all  --  any    eth0    anywhere             anywhere            [goto]  connmark match  0x0/0x1000000

Chain QOS_MARK_eth0 (1 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 DSCP       tcp  --  any    any     Lotus.lan            anywhere             /* Testing to see if ctinfo is working */ DSCP set 0x22
    0     0 DSCP       udp  --  any    any     Lotus.lan            anywhere             /* Testing to see if ctinfo is working */ DSCP set 0x22
    0     0 DSCP       tcp  --  any    any     Mike_WiiU.lan        anywhere             multiport dports  !www,https /* Michael's Wii U DSCP */ DSCP set 0x22
    0     0 DSCP       udp  --  any    any     Mike_WiiU.lan        anywhere             multiport dports  !80,https /* Michael's Wii U DSCP */ DSCP set 0x22
    0     0 DSCP       tcp  --  any    any     Hayden_WiiU.lan      anywhere             multiport dports  !www,https /* Hayden's Wii U DSCP */ DSCP set 0x22
    0     0 DSCP       udp  --  any    any     Hayden_WiiU.lan      anywhere             multiport dports  !80,https /* Hayden's Wii U DSCP */ DSCP set 0x22
    0     0 DSCP       tcp  --  any    any     Haydens_Switch.lan   anywhere             multiport dports  !www,https /* Hayden's Switch DSCP */ DSCP set 0x22
    0     0 DSCP       udp  --  any    any     Haydens_Switch.lan   anywhere             multiport dports  !80,https /* Hayden's Switch DSCP */ DSCP set 0x22
    0     0 CONNMARK   all  --  any    any     anywhere             anywhere             CONNMARK DSCP set 0xfc000000/0x1000000

What caught my eye is that the only rule in POSTROUTING looks like it is being skipped. I tried leaving out the connmark match in the last rule (so it would be 'ipt -t mangle -A POSTROUTING -o $IFACE -g QOS_MARK_${IFACE}' and the result is the same. I added my PC to the list of rules just so I should always have some traffic that would be matched.

I'm at a loss as to where to go from here, so I'll take a break and hopefully someone more experienced can point me in the right direction.

Thanks.

EDIT April 27, 2020 - Updated script because some users have found this thread and it isn't compatible with some sqm-script changes.

Despite the fact that CAKE/sqm-scripts happily operated on eth0 for years on my device, my problem is that VLANs actually matter for the iptables rules and I needed to change the interface to eth0.2.

I still had some issues with act_ctinfo after sqm was restarted, but after stopping sqm, restarting firewall, then starting sqm everything seems to be happy. My nephew decided to play some Smash while I was troubleshooting and I could see:

Chain POSTROUTING (policy ACCEPT 19M packets, 16G bytes)
num   pkts bytes target     prot opt in     out     source               destination
1    9684K 1107M QOS_MARK_eth0.2  all  --  any    eth0.2  anywhere             anywhere            [goto]  connmark match  0x0/0x1000000

Chain QOS_MARK_eth0.2 (1 references)
num   pkts bytes target     prot opt in     out     source               destination
6     326K   64M DSCP       udp  --  any    any     Haydens_Switch.lan   anywhere             multiport dports  !80,https /* Hayden's Switch DSCP */ DSCP set 0x22

filter parent cacf: protocol all pref 10 u32 chain 0 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:1 not_in_hw
  match 00000000/00000000 at 0
        action order 1: ctinfo zone 0 pipe
         index 1 ref 1 bind 1 dscp 0xfc000000 0x01000000 installed 69026 sec used 0 sec DSCP set 116286 error 0 CPMARK set 0
        Action statistics:
        Sent 1372398791 bytes 10902552 pkt (dropped 0, overlimits 0 requeues 0)
        backlog 0b 0p requeues 0

filter parent ffff: protocol all pref 10 u32 chain 0 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:1 not_in_hw
  match 00000000/00000000 at 0
        action order 1: ctinfo zone 0 pipe
         index 2 ref 1 bind 1 dscp 0xfc000000 0x01000000 installed 69025 sec used 0 sec DSCP set 178333 error 0 CPMARK set 0
        Action statistics:
        Sent 21732283944 bytes 16317329 pkt (dropped 0, overlimits 0 requeues 0)
        backlog 0b 0p requeues 0

I'm going to mark this as solved, but I do have a question (@moeller0 might be able to answer this): The switch info as shown by LuCI shows cpu (eth0) as tagged and WAN as untagged (the only ports on VLAN 2). Do I need to factor this into the overhead for CAKE?

IF you use cake to shape your WAN link internal VLANs typically have no bearing on the configuration. As a rule of thumb you need to describe the per packet overhead on the bottleneck link veridically. If you have a >>100 Mbps WAN link, but a 100Mbps fast-ethernet LAN switch you will need to take this into account (probably with overhead 38 or with VLAN tags on LAN even 42 bytes). As long as LAN >> WAN speeds the interal VLAN tag on the LAN link should not matter.

Hope that helps.

1 Like

It does, thanks.

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