|
#!/usr/bin/env bash |
|
set -euo pipefail |
|
|
|
# Tailscale exit node / subnet router optimizer for Debian/Ubuntu |
|
# - Enables UDP GRO forwarding optimization on the default network device |
|
# - Persists ethtool settings via networkd-dispatcher when available |
|
# - Enables IPv4 and IPv6 forwarding |
|
# - Enables TCP BBR congestion control |
|
# |
|
# Run as root: |
|
# sudo bash tailscale-exit-node-optimize.sh |
|
|
|
if [[ "${EUID}" -ne 0 ]]; then |
|
echo "Please run as root, for example:" |
|
echo " sudo bash $0" |
|
exit 1 |
|
fi |
|
|
|
echo "==> Detecting default network interface..." |
|
|
|
NETDEV="$( |
|
ip -o route get 8.8.8.8 2>/dev/null | awk '{for (i=1;i<=NF;i++) if ($i=="dev") print $(i+1); exit}' |
|
)" |
|
|
|
if [[ -z "${NETDEV}" ]]; then |
|
echo "Could not detect default network interface." |
|
echo "Make sure the VM has a default IPv4 route." |
|
exit 1 |
|
fi |
|
|
|
echo "Default network interface: ${NETDEV}" |
|
|
|
echo |
|
echo "==> Installing required tools..." |
|
|
|
apt-get update |
|
apt-get install -y ethtool |
|
|
|
echo |
|
echo "==> Applying Tailscale network offload optimizations..." |
|
|
|
if ethtool -K "${NETDEV}" rx-udp-gro-forwarding on rx-gro-list off; then |
|
echo "Applied ethtool optimizations to ${NETDEV}" |
|
else |
|
echo "Warning: failed to apply one or more ethtool settings." |
|
echo "This can happen if the NIC/driver/kernel does not support them." |
|
fi |
|
|
|
echo |
|
echo "==> Persisting ethtool settings across reboot..." |
|
|
|
if systemctl list-unit-files networkd-dispatcher.service >/dev/null 2>&1; then |
|
apt-get install -y networkd-dispatcher |
|
|
|
mkdir -p /etc/networkd-dispatcher/routable.d |
|
|
|
cat > /etc/networkd-dispatcher/routable.d/50-tailscale <<EOF |
|
#!/bin/sh |
|
ethtool -K ${NETDEV} rx-udp-gro-forwarding on rx-gro-list off |
|
EOF |
|
|
|
chmod 755 /etc/networkd-dispatcher/routable.d/50-tailscale |
|
|
|
systemctl enable --now networkd-dispatcher || true |
|
|
|
echo "Testing persistent ethtool hook..." |
|
if /etc/networkd-dispatcher/routable.d/50-tailscale; then |
|
echo "Persistent ethtool hook installed successfully." |
|
else |
|
echo "Warning: persistent ethtool hook test failed." |
|
fi |
|
else |
|
echo "networkd-dispatcher is not available on this system." |
|
echo "Skipping persistent ethtool hook." |
|
echo "The ethtool optimization was applied for the current boot only." |
|
fi |
|
|
|
echo |
|
echo "==> Enabling IPv4 and IPv6 forwarding..." |
|
|
|
SYSCTL_FILE="/etc/sysctl.d/99-tailscale-exit-node.conf" |
|
|
|
cat > "${SYSCTL_FILE}" <<'EOF' |
|
# Tailscale exit node / subnet router forwarding |
|
net.ipv4.ip_forward = 1 |
|
net.ipv6.conf.all.forwarding = 1 |
|
|
|
# TCP BBR |
|
net.core.default_qdisc = fq |
|
net.ipv4.tcp_congestion_control = bbr |
|
EOF |
|
|
|
sysctl -p "${SYSCTL_FILE}" |
|
|
|
echo |
|
echo "==> Checking BBR status..." |
|
|
|
BBR_STATUS="$(sysctl -n net.ipv4.tcp_congestion_control || true)" |
|
|
|
if [[ "${BBR_STATUS}" == "bbr" ]]; then |
|
echo "BBR is enabled." |
|
else |
|
echo "Warning: BBR does not appear to be enabled." |
|
echo "Current congestion control: ${BBR_STATUS}" |
|
fi |
|
|
|
echo |
|
echo "==> Checking kernel version..." |
|
|
|
KERNEL_VERSION="$(uname -r)" |
|
echo "Kernel: ${KERNEL_VERSION}" |
|
|
|
echo |
|
echo "==> Optional firewalld masquerade workaround..." |
|
|
|
if command -v firewall-cmd >/dev/null 2>&1 && systemctl is-active --quiet firewalld; then |
|
firewall-cmd --permanent --add-masquerade |
|
firewall-cmd --reload |
|
echo "Enabled firewalld masquerading." |
|
else |
|
echo "firewalld is not active; skipping masquerade workaround." |
|
fi |
|
|
|
echo |
|
echo "==> Done." |
|
|
|
echo |
|
echo "Recommended next checks:" |
|
echo " ethtool -k ${NETDEV} | grep -E 'rx-udp-gro-forwarding|rx-gro-list'" |
|
echo " sysctl net.ipv4.ip_forward" |
|
echo " sysctl net.ipv6.conf.all.forwarding" |
|
echo " sysctl net.ipv4.tcp_congestion_control" |
|
echo " tailscale status" |