Despite the Linux Foundation's historical position on TCP Offload Engine (TOE), many modern NICs still enable these features by default. When deploying Debian servers at scale through FAI (Fully Automatic Installation), we need a maintenance-friendly solution that works across diverse hardware.
The cleanest approach uses /etc/network/interfaces
with pre-up hooks. Create a script in /etc/network/if-pre-up.d/
:
#!/bin/sh
[ "$IFACE" != "lo" ] && ethtool -K $IFACE \
tso off gso off gro off lro off sg off \
tx off rx off rxvlan off txvlan off \
ntuple off receive-hashing off
exit 0
Make it executable:
chmod +x /etc/network/if-pre-up.d/disable-offload
The script above gracefully handles unsupported options. Testing on various NICs reveals:
# Intel X710 (supports all):
Settings for eth0:
tcp-segmentation-offload: off
generic-segmentation-offload: off
[...]
# Older Broadcom (partial support):
Cannot change tso
Cannot change gso
[...]
For FAI deployments, include this in your class configuration:
FAI_DEBOOTSTRAP="ethtool"
FAI_CONFIG_DECLARE="DISABLE_OFFLOAD"
FAI_CONFIG_SRC='debconf-set netcfg/disable_offload boolean true'
Then add to your package list:
PACKAGES install ethtool
For systems using networkd:
[Match]
Name=en*
[Link]
TCPSegmentationOffload=no
GenericSegmentationOffload=no
[Network]
DHCP=yes
Contrary to common expectation, Linux lacks a global TOE disable switch in sysctl. The per-interface approach via ethtool remains the most reliable method.
Post-configuration checks:
# Check current settings
ethtool -k eth0 | grep -E 'tso|gso|lro|gro'
# Test with packet capture
tcpdump -i eth0 -nn -s0 -w no-offload.pcap
# Analyze with wireshark looking for abnormal segment sizes
Despite the Linux Foundation's historical reservations about TCP Offload Engine (TOE), modern Debian installations frequently come with various offloading features enabled by default on newer NICs. This can cause unexpected behavior in production environments where consistent network performance is critical.
The standard approach using ethtool
commands in /etc/network/interfaces
has several pain points:
# Typical but problematic approach
auto eth0
iface eth0 inet dhcp
pre-up /sbin/ethtool -K eth0 tx off sg off tso off gso off gro off lro off
This becomes unwieldy when managing multiple NICs or dealing with heterogeneous hardware where some offload features might not exist.
For a cleaner implementation that survives package upgrades and follows Debian conventions:
# Install required package
apt-get install ethtool
# Create a persistent configuration file
cat > /etc/network/if-pre-up.d/disable_toe << 'EOF'
#!/bin/sh
[ "$IFACE" != "lo" ] || exit 0
for feature in tx sg tso gso gro lro; do
ethtool -K "$IFACE" "$feature" off 2>/dev/null
done
EOF
chmod +x /etc/network/if-pre-up.d/disable_toe
For those using Fully Automatic Installation (FAI), add this to your class configuration:
# In your FAI class config file
PACKAGES ethtool
DIRECTORY /etc/network/if-pre-up.d/
COPY disable_toe /etc/network/if-pre-up.d/
EXEC chmod +x /etc/network/if-pre-up.d/disable_toe
The solution above includes 2>/dev/null
to silently ignore unsupported features. For better debugging, you might want to modify the script:
#!/bin/sh
[ "$IFACE" != "lo" ] || exit 0
for feature in tx sg tso gso gro lro; do
if ! ethtool -K "$IFACE" "$feature" off 2>&1 | grep -q "not supported"; then
logger -t disable_toe "Disabled $feature on $IFACE"
fi
done
After implementation, verify with:
ethtool -k eth0 | grep -E 'tx-|sg-|tso-|gso-|gro-|lro-'
Expected output should show all relevant features as off
.