1+ #! /bin/bash
2+ set -euo pipefail
3+
4+ NETDEV_BUDGET=" 600"
5+ PREFERRED_RX=" 4096"
6+ FALLBACK_RX=" 2047"
7+
8+ DISPATCHER_SCRIPT=" /etc/NetworkManager/dispatcher.d/45-ethtool"
9+ SYSCTL_FILE=" /etc/sysctl.d/99-netdev-budget.conf"
10+
11+ if [[ $EUID -ne 0 ]]; then
12+ echo " Run this as root."
13+ exit 1
14+ fi
15+
16+ command -v ethtool > /dev/null || {
17+ echo " ethtool is not installed. Install it first."
18+ exit 1
19+ }
20+
21+ get_physical_interfaces () {
22+ for path in /sys/class/net/* ; do
23+ iface=" $( basename " $path " ) "
24+
25+ [[ " $iface " == " lo" ]] && continue
26+ [[ -d " $path /device" ]] || continue
27+
28+ echo " $iface "
29+ done
30+ }
31+
32+ detect_rx_size () {
33+ local iface=" $1 "
34+
35+ if ethtool -G " $iface " rx " $PREFERRED_RX " > /dev/null 2>&1 ; then
36+ echo " $PREFERRED_RX "
37+ elif ethtool -G " $iface " rx " $FALLBACK_RX " > /dev/null 2>&1 ; then
38+ echo " $FALLBACK_RX "
39+ else
40+ echo " "
41+ fi
42+ }
43+
44+ echo " Detecting physical interfaces..."
45+ mapfile -t INTERFACES < <( get_physical_interfaces)
46+
47+ if [[ ${# INTERFACES[@]} -eq 0 ]]; then
48+ echo " No physical interfaces found."
49+ exit 1
50+ fi
51+
52+ echo " Found interfaces: ${INTERFACES[*]} "
53+ echo
54+
55+ echo " Testing and applying RX ring settings..."
56+
57+ declare -A RX_SIZE_BY_IFACE
58+
59+ for iface in " ${INTERFACES[@]} " ; do
60+ rx_size=" $( detect_rx_size " $iface " ) "
61+
62+ if [[ -n " $rx_size " ]]; then
63+ RX_SIZE_BY_IFACE[" $iface " ]=" $rx_size "
64+ echo " $iface : using RX ring size $rx_size "
65+ else
66+ echo " $iface : could not set RX ring to $PREFERRED_RX or $FALLBACK_RX , skipping"
67+ fi
68+ done
69+
70+ echo
71+ echo " Setting net.core.netdev_budget=$NETDEV_BUDGET "
72+ sysctl -w net.core.netdev_budget=" $NETDEV_BUDGET "
73+
74+ echo
75+ echo " Writing static dispatcher script: $DISPATCHER_SCRIPT "
76+
77+ [[ -f " $DISPATCHER_SCRIPT " ]] && cp " $DISPATCHER_SCRIPT " " $DISPATCHER_SCRIPT .bak"
78+
79+ cat > " $DISPATCHER_SCRIPT " << 'EOF '
80+ #!/bin/bash
81+ EOF
82+
83+ for iface in " ${INTERFACES[@]} " ; do
84+ rx_size=" ${RX_SIZE_BY_IFACE[$iface]:- } "
85+
86+ [[ -z " $rx_size " ]] && continue
87+
88+ cat >> " $DISPATCHER_SCRIPT " << EOF
89+ if [ "\$ 1" == "$iface " ] && [ "\$ 2" == "up" ]; then
90+ ethtool -G "$iface " rx "$rx_size "
91+ fi
92+
93+ EOF
94+ done
95+
96+ chmod +x " $DISPATCHER_SCRIPT "
97+
98+ echo " Writing persistent sysctl config: $SYSCTL_FILE "
99+
100+ [[ -f " $SYSCTL_FILE " ]] && cp " $SYSCTL_FILE " " $SYSCTL_FILE .bak"
101+
102+ cat > " $SYSCTL_FILE " << EOF
103+ net.core.netdev_budget=$NETDEV_BUDGET
104+ EOF
105+
106+ if ! sysctl -p " $SYSCTL_FILE " > /dev/null; then
107+ echo " Warning: failed to apply sysctl settings from $SYSCTL_FILE " >&2
108+ fi
109+
110+ echo
111+ echo " Validating applied settings..."
112+ ACTUAL_BUDGET=" $( sysctl -n net.core.netdev_budget) "
113+ if [[ " $ACTUAL_BUDGET " == " $NETDEV_BUDGET " ]]; then
114+ echo " ✓ net.core.netdev_budget correctly set to $NETDEV_BUDGET "
115+ else
116+ echo " ✗ Warning: net.core.netdev_budget is $ACTUAL_BUDGET (expected $NETDEV_BUDGET )"
117+ fi
118+
119+ echo
120+ echo " Done."
121+ echo " Dispatcher script created at: $DISPATCHER_SCRIPT "
122+ echo " Sysctl persistence created at: $SYSCTL_FILE "
123+ [[ -f " $DISPATCHER_SCRIPT .bak" ]] && echo " Backup saved at: $DISPATCHER_SCRIPT .bak"
124+ [[ -f " $SYSCTL_FILE .bak" ]] && echo " Backup saved at: $SYSCTL_FILE .bak"
0 commit comments