Tailscale version 1.54 or later used with OpenWrt 24.10 or later (which uses kernel 6.6) enables UDP throughput improvements via transport layer offloading.
Namely, tuning two features may show improved throughput:
rx-udp-gro-forwarding
: Enables UDP Generic Receive Offload (GRO) forwarding, which aggregates incoming UDP packets to reduce CPU overhead on receive.rx-gro-list
: If disabled (off), it prevents multiple flows from being aggregated simultaneously which simplifies flow handling and performance on some workloads.
Note
These changes should be applied to the physical device(s) which will actually be performing the UDP encapsulation of tailscale traffic.
- Install
ethtool
opkg update
opkg install ethtool
- Apply the changes:
Note
Substitute eth1
below for the device that routes your Tailscale traffic. In most configurations, this is your WAN device.
ethtool -K eth1 rx-gro-list off
ethtool -K eth1 rx-udp-gro-forwarding on
- Test the changes before and after before committing them permanently with something similar to the following commands.
You want to verify:
- Packet aggregation is working as measured by reduced packets/sec on the wire with GRO enabled (verify with tools like:
ethtool -S <interface> | grep udp
ornetstat -su
) - CPU usage is reduced. Lower CPU usage on the receiver compared to the same test with
rx-udp-gro-forwarding
turned off - High throughput is achieved near line rate (e.g., 1 Gbps, 10Gbps, etc) without packetloss.
Note
You will need the iperf3
package installed for this. The sender side flags below are just a suggestion. Full iperf3
usage is outside the scope of this document.
Receiver
iperf3 --server
Sender
iperf3 --client <remote_addr> --udp --bitrate 1G --length 1400 --time 10
If you're satisfied with the results and want it to persist across reboots.
- Create
/etc/config/ethtool
ether using uci or by creating the file manually. The following example will useuci
:
Note
Substitute eth1
below for the device that routes your Tailscale traffic. In most configurations, this is your WAN device.
touch /etc/config/ethtool
uci set ethtool.eth1=device
uci set ethtool.eth1.rx_gro_list='off'
uci set ethtool.eth1.rx_udp_gro_forwarding='on'
uci commit
- Create the following file in
/etc/hotplug.d/iface/90-ethtool
#!/bin/sh
# shellcheck disable=SC3043
#
# Author: Josh Enders <[email protected]>
# License: CC BY-NC 4.0
# https://gist.github.com/joshenders/1baa9de07c1b7af489f14c30d4667e40
[ "${ACTION}" = "ifup" ] || exit 0
# shellcheck source=/dev/null
. /lib/functions.sh
config_load ethtool
log_crit() { logger -t "$0" -p crit "$1"; }
log_info() { logger -t "$0" -p info "$1"; }
apply_settings() {
local config feature ifname option value
ifname="$1"
config=$(uci show ethtool."${ifname}" | sed -n "s/^ethtool.${ifname}\.\([^=]*\)=.*/\1/p")
for option in ${config}; do
config_get value "${ifname}" "${option}"
feature=$(echo "${option}" | tr '_' '-')
if [ -n "${value}" ]; then
{
ethtool -K "${ifname}" "${feature}" "${value}" \
&& log_info "${feature} set to ${value} on ${ifname}";
} || log_crit "Failed to set ${feature} to ${value} on ${ifname}"
else
log_crit "Failed to set ${feature} to ${value} on ${ifname}"
fi
done
}
config_foreach apply_settings device
- Append
/etc/hotplug.d/iface/90-ethtool
to/etc/sysupgrade.conf
to preserve this file during upgrades.
echo '/etc/hotplug.d/iface/90-ethtool' >> /etc/sysupgrade.conf
Hi Josh. Thanks for this! I think I may have found a couple of issues.
I think it should be like this:
As per the tailscale kb (https://tailscale.com/kb/1320/performance-best-practices#ethtool-configuration), this can be automated too.