From e4447f31973d52ec21d65c109a09ef659424af17 Mon Sep 17 00:00:00 2001 From: bitthief Date: Sat, 20 May 2023 01:32:31 +0300 Subject: [PATCH] qca-nss-ecm: introduce helper to disable offloads Experimental fix for SSL/TLS handshake failures and packet fragmentation / MTU issues observed after enabling the ECM module. --- qca/qca-nss-ecm/Makefile | 4 +- .../files/disable_offloads.hotplug | 187 +++++++++++++++++ qca/qca-nss-ecm/files/disable_offloads.sh | 189 ++++++++++++++++++ qca/qca-nss-ecm/files/qca-nss-ecm.uci | 3 + 4 files changed, 382 insertions(+), 1 deletion(-) create mode 100755 qca/qca-nss-ecm/files/disable_offloads.hotplug create mode 100755 qca/qca-nss-ecm/files/disable_offloads.sh diff --git a/qca/qca-nss-ecm/Makefile b/qca/qca-nss-ecm/Makefile index 8398576..2422ebe 100644 --- a/qca/qca-nss-ecm/Makefile +++ b/qca/qca-nss-ecm/Makefile @@ -47,14 +47,16 @@ This package contains the QCA NSS Enhanced Connection Manager endef define KernelPackage/qca-nss-ecm/install - $(INSTALL_DIR) $(1)/etc/firewall.d $(1)/etc/init.d $(1)/usr/bin $(1)/lib/netifd/offload $(1)/etc/config $(1)/etc/uci-defaults $(1)/etc/sysctl.d + $(INSTALL_DIR) $(1)/etc/firewall.d $(1)/etc/init.d $(1)/usr/bin $(1)/lib/netifd/offload $(1)/etc/config $(1)/etc/uci-defaults $(1)/etc/sysctl.d $(1)/etc/hotplug.d/net $(INSTALL_DATA) ./files/qca-nss-ecm.firewall $(1)/etc/firewall.d/qca-nss-ecm $(INSTALL_BIN) ./files/qca-nss-ecm.init $(1)/etc/init.d/qca-nss-ecm $(INSTALL_BIN) ./files/ecm_dump.sh $(1)/usr/bin/ + $(INSTALL_BIN) ./files/disable_offloads.sh $(1)/usr/bin/ $(INSTALL_BIN) ./files/on-demand-down $(1)/lib/netifd/offload/on-demand-down $(INSTALL_DATA) ./files/qca-nss-ecm.uci $(1)/etc/config/ecm $(INSTALL_DATA) ./files/qca-nss-ecm.defaults $(1)/etc/uci-defaults/99-qca-nss-ecm $(INSTALL_BIN) ./files/qca-nss-ecm.sysctl $(1)/etc/sysctl.d/qca-nss-ecm.conf + $(INSTALL_BIN) ./files/disable_offloads.hotplug $(1)/etc/hotplug.d/net/99-disable_offloads endef EXTRA_CFLAGS+= \ diff --git a/qca/qca-nss-ecm/files/disable_offloads.hotplug b/qca/qca-nss-ecm/files/disable_offloads.hotplug new file mode 100755 index 0000000..01c5647 --- /dev/null +++ b/qca/qca-nss-ecm/files/disable_offloads.hotplug @@ -0,0 +1,187 @@ +#!/bin/sh + +[ "$ACTION" != "add" ] && exit + +function log() +{ + local status="$1" + local feature="$2" + local interface="$3" + + if [ $status -eq 0 ]; then + logger "[ethtool] $feature: disabled on $interface" + fi + + if [ $status -eq 1 ]; then + logger -s "[ethtool] $feature: failed to disable on $interface" + fi + + if [ $status -gt 1 ]; then + logger "[ethtool] $feature: no changes performed on $interface" + fi +} + +function interface_is_virtual() +{ + local interface="$1" + [ -d /sys/devices/virtual/net/"$interface"/ ] || return 1 + return 0 +} + +function get_base_interface() +{ + local interface="$1" + echo "$interface" | grep -Eo '^[a-z]*[0-9]*' 2>/dev/null || return 1 + return 0 +} + +function disable_offloads() +{ + local interface="$1" + local features + local cmd + + # Check if we can change features + if ethtool -k $interface 1>/dev/null 2>/dev/null; then + # Filter whitespaces + # Get only enabled/not fixed features + # Filter features that are only changeable by global keyword + # Filter empty lines + # Cut to First column + features=$(ethtool -k "$interface" | awk '{$1=$1;print}' \ + | grep -E '^.+: on$' \ + | grep -v -E '^tx-checksum-.+$' \ + | grep -v -E '^tx-scatter-gather.+$' \ + | grep -v -E '^tx-tcp.+segmentation.+$' \ + | grep -v -E '^tx-udp-fragmentation$' \ + | grep -v -E '^tx-generic-segmentation$' \ + | grep -v -E '^rx-gro$' \ + | grep -v -E '^rx-gro$' \ + | grep -v -E '^$' \ + | cut -d: -f1) + + # Replace feature name by global keyword + features=$(echo "$features" | sed -e s/rx-checksumming/rx/ \ + -e s/tx-checksumming/tx/ \ + -e s/scatter-gather/sg/ \ + -e s/tcp-segmentation-offload/tso/ \ + -e s/udp-fragmentation-offload/ufo/ \ + -e s/generic-segmentation-offload/gso/ \ + -e s/generic-receive-offload/gro/ \ + -e s/large-receive-offload/lro/ \ + -e s/rx-vlan-offload/rxvlan/ \ + -e s/tx-vlan-offload/txvlan/ \ + -e s/ntuple-filters/ntuple/ \ + -e s/receive-hashing/rxhash/) + + # Check if we can disable anything + if [ -z "$features" ]; then + logger "[ethtool] offloads: no changes performed on $interface" + return 0 + fi + + # Construct ethtool command line + cmd="-K $interface" + + for feature in $features; do + cmd="$cmd $feature off" + done + + # Try to disable offloads + ethtool $cmd 1>/dev/null 2>/dev/null + log $? "Offloads" "$interface" + + else + log $? "Offloads" "$interface" + fi +} + +function disable_flow_control() +{ + local interface="$1" + local features + local cmd + + # Check if we can change settings + if ethtool -a $interface 1>/dev/null 2>/dev/null; then + # Construct ethtool command line + cmd="-A $interface autoneg off tx off rx off" + + # Try to disable flow control + ethtool $cmd 1>/dev/null 2>/dev/null + log $? "Flow Control" "$interface" + + else + log $? "Flow Control" "$interface" + fi +} + +function disable_interrupt_moderation() +{ + local interface="$1" + local features + local cmd + + # Check if we can change settings + if ethtool -c $interface 1>/dev/null 2>/dev/null; then + # Construct ethtool command line + cmd="-C $interface adaptive-tx off adaptive-rx off" + + # Try to disable adaptive interrupt moderation + ethtool $cmd 1>/dev/null 2>/dev/null + log $? "Adaptive Interrupt Moderation" "$interface" + + features=$(ethtool -c $interface | awk '{$1=$1;print}' \ + | grep -v -E '^.+: 0$|Adaptive|Coalesce' \ + | grep -v -E '^$' \ + | cut -d: -f1) + + # Check if we can disable anything + if [ -z "$features" ]; then + logger "[ethtool] Interrupt Moderation: no changes performed on $interface" + return 0 + fi + + # Construct ethtool command line + cmd="-C $interface" + + for feature in $features; do + cmd="$cmd $feature 0" + done + + # Try to disable interrupt Moderation + ethtool $cmd 1>/dev/null 2>/dev/null + log $? "Interrupt Moderation" "$interface" + + else + log $? "Interrupt Moderation" "$interface" + fi +} + +function disable_interface_offloads() { + #local interface=$(get_base_interface "$1") + #{ [ -z "$interface" ] || interface_is_virtual "$interface"; } && exit 0 + + local interface="$1" + + local disable_offloads="$(uci get ecm.@general[0].disable_offloads)" + if [ "$disable_offloads" -eq 1 ]; then + disable_offloads "$interface" + fi + + local disable_flow_control="$(uci get ecm.@general[0].disable_flow_control)" + if [ "$disable_flow_control" -eq 1 ]; then + disable_flow_control "$interface" + fi + + local disable_interrupt_moderation="$(uci get ecm.@general[0].disable_interrupt_moderation)" + if [ "$disable_interrupt_moderation" -eq 1 ]; then + disable_interrupt_moderation "$interface" + fi +} + +if [ "$ACTION" = add ]; then + disable_interface_offloads "$INTERFACE" +fi + +exit 0 diff --git a/qca/qca-nss-ecm/files/disable_offloads.sh b/qca/qca-nss-ecm/files/disable_offloads.sh new file mode 100755 index 0000000..9f8394f --- /dev/null +++ b/qca/qca-nss-ecm/files/disable_offloads.sh @@ -0,0 +1,189 @@ +#!/bin/sh +# +# Helper script which uses ethtool to disable (most) +# interface offloads, if possible. +# +# Reference: +# https://forum.openwrt.org/t/how-to-make-ethtool-setting-persistent-on-br-lan/6433/14 +# + +function log() +{ + local status="$1" + local feature="$2" + local interface="$3" + + if [ $status -eq 0 ]; then + logger "[ethtool] $feature: disabled on $interface" + fi + + if [ $status -eq 1 ]; then + logger -s "[ethtool] $feature: failed to disable on $interface" + fi + + if [ $status -gt 1 ]; then + logger "[ethtool] $feature: no changes performed on $interface" + fi +} + +function interface_is_virtual() +{ + local interface="$1" + [ -d /sys/devices/virtual/net/"$interface"/ ] || return 1 + return 0 +} + +function get_base_interface() +{ + local interface="$1" + echo "$interface" | grep -Eo '^[a-z]*[0-9]*' 2>/dev/null || return 1 + return 0 +} + +function disable_offloads() +{ + local interface="$1" + local features + local cmd + + # Check if we can change features + if ethtool -k $interface 1>/dev/null 2>/dev/null; then + + # Filter whitespaces + # Get only enabled/not fixed features + # Filter features that are only changeable by global keyword + # Filter empty lines + # Cut to First column + features=$(ethtool -k "$interface" | awk '{$1=$1;print}' \ + | grep -E '^.+: on$' \ + | grep -v -E '^tx-checksum-.+$' \ + | grep -v -E '^tx-scatter-gather.+$' \ + | grep -v -E '^tx-tcp.+segmentation.+$' \ + | grep -v -E '^tx-udp-fragmentation$' \ + | grep -v -E '^tx-generic-segmentation$' \ + | grep -v -E '^rx-gro$' \ + | grep -v -E '^rx-gro$' \ + | grep -v -E '^$' \ + | cut -d: -f1) + + # Replace feature name by global keyword + features=$(echo "$features" | sed -e s/rx-checksumming/rx/ \ + -e s/tx-checksumming/tx/ \ + -e s/scatter-gather/sg/ \ + -e s/tcp-segmentation-offload/tso/ \ + -e s/udp-fragmentation-offload/ufo/ \ + -e s/generic-segmentation-offload/gso/ \ + -e s/generic-receive-offload/gro/ \ + -e s/large-receive-offload/lro/ \ + -e s/rx-vlan-offload/rxvlan/ \ + -e s/tx-vlan-offload/txvlan/ \ + -e s/ntuple-filters/ntuple/ \ + -e s/receive-hashing/rxhash/) + + # Check if we can disable anything + if [ -z "$features" ]; then + logger "[ethtool] offloads: no changes performed on $interface" + return 0 + fi + + # Construct ethtool command line + cmd="-K $interface" + + for feature in $features; do + cmd="$cmd $feature off" + done + + # Try to disable offloads + ethtool $cmd 1>/dev/null 2>/dev/null + log $? "Offloads" "$interface" + + else + log $? "Offloads" "$interface" + fi +} + +function disable_flow_control() +{ + local interface="$1" + local features + local cmd + + # Check if we can change settings + if ethtool -a $interface 1>/dev/null 2>/dev/null; then + + # Construct ethtool command line + cmd="-A $interface autoneg off tx off rx off" + + # Try to disable flow control + ethtool $cmd 1>/dev/null 2>/dev/null + log $? "Flow Control" "$interface" + + else + log $? "Flow Control" "$interface" + fi +} + +function disable_interrupt_moderation() +{ + local interface="$1" + local features + local cmd + + # Check if we can change settings + if ethtool -c $interface 1>/dev/null 2>/dev/null; then + # Construct ethtool command line + cmd="-C $interface adaptive-tx off adaptive-rx off" + + # Try to disable adaptive interrupt moderation + ethtool $cmd 1>/dev/null 2>/dev/null + log $? "Adaptive Interrupt Moderation" "$interface" + + features=$(ethtool -c $interface | awk '{$1=$1;print}' \ + | grep -v -E '^.+: 0$|Adaptive|Coalesce' \ + | grep -v -E '^$' \ + | cut -d: -f1) + + # Check if we can disable anything + if [ -z "$features" ]; then + logger "[ethtool] Interrupt Moderation: no changes performed on $interface" + return 0 + fi + + # Construct ethtool command line + cmd="-C $interface" + + for feature in $features; do + cmd="$cmd $feature 0" + done + + # Try to disable interrupt Moderation + ethtool $cmd 1>/dev/null 2>/dev/null + log $? "Interrupt Moderation" "$interface" + + else + log $? "Interrupt Moderation" "$interface" + fi +} + +function main() +{ + for interface in /sys/class/net/*; do + interface=$(basename $interface) + + #interface=$(get_base_interface "$interface") + #{ [ -z "$interface" ] || interface_is_virtual "$interface"; } && exit 0 + + # Skip Loopback + if [ $interface == lo ]; then + continue + fi + + disable_offloads "$interface" + disable_flow_control "$interface" + disable_interrupt_moderation "$interface" + done +} + +main + +exit 0 diff --git a/qca/qca-nss-ecm/files/qca-nss-ecm.uci b/qca/qca-nss-ecm/files/qca-nss-ecm.uci index 59028d0..20c02bb 100644 --- a/qca/qca-nss-ecm/files/qca-nss-ecm.uci +++ b/qca/qca-nss-ecm/files/qca-nss-ecm.uci @@ -3,3 +3,6 @@ config ecm 'global' config general option enable_bridge_filtering '1' + option disable_offloads '0' + option disable_flow_control '0' + option disable_interrupt_moderation '0'