-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathnetopt
More file actions
456 lines (391 loc) · 17.2 KB
/
netopt
File metadata and controls
456 lines (391 loc) · 17.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
#!/bin/bash
set -Eeu
# =============================================================================================
# Linux Network Optimizer
# Automatic network performance optimization for Linux systems using NetworkManager dispatcher.
# Licensing: MIT
# Requires: bash, bc, nftables (optional)
# Copyright (c) 2026 Mario Herrmann. All rights reserved.
# =============================================================================================
# NetworkManager automatically passes these parameters:
INTERFACE="${1:-}"
ACTION="${2:-}"
# Exit early if no interface specified
if [ -z "$INTERFACE" ]; then
exit 0
fi
# Only execute on relevant events
case "$ACTION" in
up|dhcp4-change|dhcp6-change)
;;
down|pre-down)
# Cleanup on interface down
if command -v nft >/dev/null 2>&1; then
TABLE_NAME="netopt_${INTERFACE//-/_}"
nft delete table inet "$TABLE_NAME" 2>/dev/null || true
fi
exit 0
;;
*)
exit 0
;;
esac
LOCK_FILE="/var/lock/netopt-${INTERFACE}.lock"
# Ensure lock directory exists
mkdir -p "$(dirname "$LOCK_FILE")" 2>/dev/null || true
# Acquire lock
exec 200>"$LOCK_FILE" 2>/dev/null || exit 0
if ! flock -n 200 2>/dev/null; then
echo "Another instance is running for $INTERFACE, waiting..." >&2
flock 200 2>/dev/null || exit 0
fi
# Cleanup lock on exit (even on Ctrl+C or kill)
trap 'flock -u 200 2>/dev/null || true; rm -f "$LOCK_FILE" 2>/dev/null || true' EXIT INT TERM
# Determine CONNECTION from the passed interface
CONNECTION=$(nmcli -g GENERAL.CONNECTION device show "$INTERFACE" 2>/dev/null || true)
# Exit if no interface or connection
if [ -z "$INTERFACE" ] || [ -z "$CONNECTION" ]; then
exit 0
fi
# Detect interface type via NetworkManager device type
INTERFACE_TYPE=$(LC_ALL=C nmcli -g GENERAL.TYPE device show "$INTERFACE" 2>/dev/null || true)
# Determine gateway and IP (with explicit error handling)
GATEWAY=$(ip route show default dev "$INTERFACE" 2>/dev/null | awk '{print $3}' | head -n 1 || true)
GATEWAY6=$(ip -6 route show default dev "$INTERFACE" 2>/dev/null | awk '{print $3}' | head -n 1 || true)
IP=$(ip -4 addr show dev "$INTERFACE" 2>/dev/null | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | head -n 1 || true)
IP6=$(ip -6 addr show dev "$INTERFACE" scope global 2>/dev/null | grep -oP '(?<=inet6\s)[0-9a-f:]+' | head -n 1 || true)
# =============================================================================================
# RTT Measurement (centralized, measured once)
# =============================================================================================
measure_rtt() {
local interface=$1
local gateway=$2
local src_ip=$3
local rtt=""
# Try to get RTT from existing connections first (prefer src IP filter)
if [ -n "$src_ip" ]; then
rtt=$(ss -tmi state established src "$src_ip" 2>/dev/null | awk '/rtt:/ {
match($0, /rtt:([0-9.]+)/, arr)
if (arr[1] != "") { sum += arr[1]; count++ }
}
END { if (count > 0) printf "%.0f\n", sum/count; else print "" }' || true)
fi
# Fallback to device-based filter if src IP failed or unavailable
if [ -z "$rtt" ] && [ -n "$interface" ]; then
rtt=$(ss -tmi state established dev "$interface" 2>/dev/null | awk '/rtt:/ {
match($0, /rtt:([0-9.]+)/, arr)
if (arr[1] != "") { sum += arr[1]; count++ }
}
END { if (count > 0) printf "%.0f\n", sum/count; else print "" }' || true)
fi
# Fallback to ping only if no established connections exist
if [ -z "$rtt" ] && [ -n "$gateway" ]; then
rtt=$(ping -c 3 -W 1 -i 0.2 -q "$gateway" 2>/dev/null | awk -F'/' '/^rtt/ {print int($5)}' || true)
fi
echo "$rtt"
}
# Measure RTT once for all optimizations
MEASURED_RTT=$(measure_rtt "$INTERFACE" "$GATEWAY" "$IP")
# Interface-specific TCP parameters
case "$INTERFACE_TYPE" in
ethernet|bond|team|bridge|vlan|infiniband|ovs-bridge|ovs-interface|ovs-port)
INITCWND=40
INITRWND=60
;;
wifi|gsm|cdma|bluetooth|wimax)
INITCWND=40
INITRWND=60
;;
vpn|wireguard|ip-tunnel|tun|pppoe)
INITCWND=20
INITRWND=30
;;
loopback)
exit 0
;;
*)
INITCWND=10
INITRWND=10
;;
esac
# Interface-specific TCP fin_timeout
case "$INTERFACE_TYPE" in
ethernet|bond|team|bridge|vlan|infiniband|ovs-bridge|ovs-interface|ovs-port)
TCP_FIN_TIMEOUT=3
;;
wifi|gsm|cdma|bluetooth|wimax)
TCP_FIN_TIMEOUT=3
;;
vpn|wireguard|ip-tunnel|tun|pppoe)
TCP_FIN_TIMEOUT=10
;;
*)
if [ -n "$MEASURED_RTT" ] && [ "$MEASURED_RTT" -gt 100 ]; then
TCP_FIN_TIMEOUT=$((MEASURED_RTT / 100 + 5))
[ "$TCP_FIN_TIMEOUT" -gt 30 ] && TCP_FIN_TIMEOUT=30
else
TCP_FIN_TIMEOUT=5
fi
;;
esac
# =============================================================================================
# Intelligent Congestion Control Selection
# =============================================================================================
select_congestion_control() {
local interface_type=$1
local rtt=$2
local available_cc="/proc/sys/net/ipv4/tcp_available_congestion_control"
local has_bbr
local has_cubic
# Explicit || true to handle grep failure
has_bbr=$(grep -o "bbr" "$available_cc" 2>/dev/null | head -1 || true)
has_cubic=$(grep -o "cubic" "$available_cc" 2>/dev/null | head -1 || true)
case "$interface_type" in
ethernet|bond|team|bridge|vlan|infiniband|ovs-bridge|ovs-interface|ovs-port)
[ -n "$has_bbr" ] && echo "bbr" && return
[ -n "$has_cubic" ] && echo "cubic" && return
echo "reno"
;;
wifi|bluetooth)
[ -n "$has_bbr" ] && echo "bbr" && return
[ -n "$has_cubic" ] && echo "cubic" && return
echo "reno"
;;
gsm|cdma|wimax)
[ -n "$has_cubic" ] && echo "cubic" && return
[ -n "$has_bbr" ] && echo "bbr" && return
echo "reno"
;;
vpn|wireguard|ip-tunnel|tun|pppoe)
[ -n "$has_cubic" ] && echo "cubic" && return
[ -n "$has_bbr" ] && echo "bbr" && return
echo "reno"
;;
*)
if [ -n "$rtt" ]; then
if [ "$rtt" -lt 50 ]; then
[ -n "$has_bbr" ] && echo "bbr" && return
elif [ "$rtt" -lt 200 ]; then
[ -n "$has_bbr" ] && echo "bbr" && return
[ -n "$has_cubic" ] && echo "cubic" && return
else
[ -n "$has_cubic" ] && echo "cubic" && return
[ -n "$has_bbr" ] && echo "bbr" && return
fi
fi
[ -n "$has_bbr" ] && echo "bbr" && return
[ -n "$has_cubic" ] && echo "cubic" && return
echo "reno"
;;
esac
}
OPTIMAL_CC=$(select_congestion_control "$INTERFACE_TYPE" "$MEASURED_RTT")
# =============================================================================================
# CAKE QDisc with Diffserv
# =============================================================================================
tc qdisc replace dev "$INTERFACE" root cake diffserv4 2>/dev/null || true
# =============================================================================================
# TOS/DSCP Marking via nftables
# =============================================================================================
setup_tos_marking() {
local interface=$1
local table_name="netopt_${interface//-/_}"
# Check if nftables is available
if ! command -v nft >/dev/null 2>&1; then
logger -t netopt "nftables not available, skipping TOS marking for $interface"
return 0
fi
# Remove old table if exists
nft delete table inet "$table_name" 2>/dev/null || true
# Create new table
if ! nft add table inet "$table_name" 2>/dev/null; then
logger -t netopt "Failed to create nftables table for $interface"
return 0
fi
# Create postrouting chain with mangle priority
if ! nft add chain inet "$table_name" postrouting \
"{ type filter hook postrouting priority mangle; policy accept; }" 2>/dev/null; then
logger -t netopt "Failed to create nftables chain for $interface"
nft delete table inet "$table_name" 2>/dev/null || true
return 0
fi
# Define Port Sets
nft add set inet "$table_name" voice_ports \
"{ type inet_service; flags interval; }" 2>/dev/null || true
nft add element inet "$table_name" voice_ports \
"{ 5060, 5004, 3074, 3478-3479, 10000-20000, 27015-27030 }" 2>/dev/null || true
nft add set inet "$table_name" interactive_ports \
"{ type inet_service; }" 2>/dev/null || true
nft add element inet "$table_name" interactive_ports \
"{ 22, 23, 3389 }" 2>/dev/null || true
nft add set inet "$table_name" video_ports \
"{ type inet_service; }" 2>/dev/null || true
nft add element inet "$table_name" video_ports \
"{ 554, 3478 }" 2>/dev/null || true
nft add set inet "$table_name" web_ports \
"{ type inet_service; }" 2>/dev/null || true
nft add element inet "$table_name" web_ports \
"{ 80, 443 }" 2>/dev/null || true
nft add set inet "$table_name" bulk_ports \
"{ type inet_service; flags interval; }" 2>/dev/null || true
nft add element inet "$table_name" bulk_ports \
"{ 6881-6889 }" 2>/dev/null || true
# Add marking rules
nft add rule inet "$table_name" postrouting \
oifname "$interface" meta l4proto udp udp dport "@voice_ports" ip dscp set ef 2>/dev/null || true
nft add rule inet "$table_name" postrouting \
oifname "$interface" meta l4proto udp udp sport "@voice_ports" ip dscp set ef 2>/dev/null || true
nft add rule inet "$table_name" postrouting \
oifname "$interface" meta l4proto \{ tcp, udp \} th dport 53 ip dscp set cs6 2>/dev/null || true
nft add rule inet "$table_name" postrouting \
oifname "$interface" meta l4proto \{ tcp, udp \} th sport 53 ip dscp set cs6 2>/dev/null || true
nft add rule inet "$table_name" postrouting \
oifname "$interface" udp dport 123 ip dscp set cs6 2>/dev/null || true
nft add rule inet "$table_name" postrouting \
oifname "$interface" udp sport 123 ip dscp set cs6 2>/dev/null || true
nft add rule inet "$table_name" postrouting \
oifname "$interface" tcp dport "@interactive_ports" ip dscp set af41 2>/dev/null || true
nft add rule inet "$table_name" postrouting \
oifname "$interface" tcp sport "@interactive_ports" ip dscp set af41 2>/dev/null || true
nft add rule inet "$table_name" postrouting \
oifname "$interface" meta l4proto \{ tcp, udp \} th dport "@video_ports" ip dscp set af31 2>/dev/null || true
nft add rule inet "$table_name" postrouting \
oifname "$interface" meta l4proto \{ tcp, udp \} th sport "@video_ports" ip dscp set af31 2>/dev/null || true
nft add rule inet "$table_name" postrouting \
oifname "$interface" tcp dport "@web_ports" ip dscp set af11 2>/dev/null || true
nft add rule inet "$table_name" postrouting \
oifname "$interface" tcp sport "@web_ports" ip dscp set af11 2>/dev/null || true
nft add rule inet "$table_name" postrouting \
oifname "$interface" tcp dport \{ 20, 21 \} ip dscp set af11 2>/dev/null || true
nft add rule inet "$table_name" postrouting \
oifname "$interface" meta l4proto \{ tcp, udp \} th dport "@bulk_ports" ip dscp set cs1 2>/dev/null || true
nft add rule inet "$table_name" postrouting \
oifname "$interface" meta l4proto \{ tcp, udp \} th sport "@bulk_ports" ip dscp set cs1 2>/dev/null || true
logger -t netopt "TOS marking configured via nftables for $interface (diffserv4)"
return 0
}
# TCP optimizations (only set once, not on every event)
if [ "$ACTION" = "up" ]; then
sysctl -q -w net.ipv4.tcp_slow_start_after_idle=0 || true
sysctl -q -w net.ipv4.tcp_congestion_control="$OPTIMAL_CC" || true
sysctl -q -w net.ipv4.tcp_notsent_lowat=131072 || true
sysctl -q -w net.ipv4.tcp_fin_timeout="$TCP_FIN_TIMEOUT" || true
sysctl -q -w net.ipv4.tcp_tw_reuse=1 || true
sysctl -q -w net.core.default_qdisc=cake || true
# ECN Configuration
KERNEL_DEFAULT_ECN=$(sysctl -n net.ipv4.tcp_ecn || echo "0")
case "$INTERFACE_TYPE" in
ethernet|bond|team|bridge|vlan|infiniband|ovs-bridge|ovs-interface|ovs-port)
if [ "$KERNEL_DEFAULT_ECN" -eq 0 ]; then
sysctl -q -w net.ipv4.tcp_ecn=1 || true
fi
;;
wifi|bluetooth)
if [ "$KERNEL_DEFAULT_ECN" -eq 0 ]; then
sysctl -q -w net.ipv4.tcp_ecn=1 || true
fi
;;
gsm|cdma|wimax|vpn|wireguard|ip-tunnel|tun|pppoe|*)
sysctl -q -w net.ipv4.tcp_ecn=1 || true
;;
esac
# Adaptive Buffer Sizing
TOTAL_RAM_BYTES=$(free -b | awk '/^Mem:/{print $2}' || echo "8589934592")
case "$INTERFACE_TYPE" in
ethernet|bond|team|bridge|vlan|infiniband|ovs-bridge|ovs-interface|ovs-port)
BUFFER_FACTOR="0.0025"
MIN_BUFFER=8388608
MAX_BUFFER=134217728
;;
wifi|bluetooth)
BUFFER_FACTOR="0.002"
MIN_BUFFER=4194304
MAX_BUFFER=67108864
;;
gsm|cdma|wimax)
BUFFER_FACTOR="0.0015"
MIN_BUFFER=2097152
MAX_BUFFER=33554432
;;
vpn|wireguard|ip-tunnel|tun|pppoe)
BUFFER_FACTOR="0.0015"
MIN_BUFFER=4194304
MAX_BUFFER=33554432
;;
*)
BUFFER_FACTOR="0.002"
MIN_BUFFER=4194304
MAX_BUFFER=67108864
if [ -n "$MEASURED_RTT" ]; then
if [ "$MEASURED_RTT" -lt 10 ]; then
BUFFER_FACTOR="0.0025"
MAX_BUFFER=134217728
elif [ "$MEASURED_RTT" -lt 50 ]; then
BUFFER_FACTOR="0.002"
MAX_BUFFER=67108864
elif [ "$MEASURED_RTT" -lt 200 ]; then
BUFFER_FACTOR="0.0015"
MAX_BUFFER=33554432
else
BUFFER_FACTOR="0.001"
MIN_BUFFER=2097152
MAX_BUFFER=16777216
fi
fi
;;
esac
RMEM_MAX=$(echo "$TOTAL_RAM_BYTES * $BUFFER_FACTOR" | bc -l | cut -d. -f1 || echo "$MAX_BUFFER")
[ "$RMEM_MAX" -lt "$MIN_BUFFER" ] && RMEM_MAX="$MIN_BUFFER"
[ "$RMEM_MAX" -gt "$MAX_BUFFER" ] && RMEM_MAX="$MAX_BUFFER"
sysctl -q -w net.core.rmem_max="$RMEM_MAX" || true
sysctl -q -w net.core.wmem_max="$RMEM_MAX" || true
sysctl -q -w net.ipv4.tcp_rmem="4096 87380 $RMEM_MAX" || true
sysctl -q -w net.ipv4.tcp_wmem="4096 65536 $RMEM_MAX" || true
# Advanced Latency Optimizations
sysctl -q -w net.ipv4.tcp_adv_win_scale=-2 || true
sysctl -q -w net.ipv4.tcp_collapse_max_bytes=6291456 || true
# Setup TOS marking
setup_tos_marking "$INTERFACE"
fi
# Optimize IPv4 route
if [ -n "$GATEWAY" ] && [ "$GATEWAY" != "--" ] && [ -n "$IP" ]; then
if [ "$ACTION" = "up" ]; then
nmcli con mod "$CONNECTION" ipv4.route-metric 600 2>/dev/null || true
nmcli con mod "$CONNECTION" ipv4.may-fail no 2>/dev/null || true
fi
if ! ip route show 2>/dev/null | grep -q "default via $GATEWAY dev $INTERFACE proto static.*initcwnd $INITCWND"; then
ip route add default via "$GATEWAY" dev "$INTERFACE" proto static src "$IP" \
metric 500 quickack 1 initcwnd "$INITCWND" initrwnd "$INITRWND" 2>/dev/null || true
fi
fi
# Optimize IPv6 route
if [ -n "$GATEWAY6" ] && [ "$GATEWAY6" != "--" ]; then
# Use regex variable for clarity
ipv6_regex='^[0-9a-f:]+$'
if [[ "$GATEWAY6" =~ $ipv6_regex ]]; then
case "$GATEWAY6" in
fe80:*|2000:*|2001:*|2002:*|2003:*|2004:*|2005:*|2006:*|2007:*|2008:*|2009:*|200a:*|200b:*|200c:*|200d:*|200e:*|200f:*|3*)
VALID_IPV6=1
;;
*)
VALID_IPV6=0
;;
esac
if [ "$VALID_IPV6" = "1" ]; then
if [ "$ACTION" = "up" ]; then
nmcli con mod "$CONNECTION" ipv6.route-metric 600 2>/dev/null || true
nmcli con mod "$CONNECTION" ipv6.may-fail no 2>/dev/null || true
fi
if ! ip -6 route show 2>/dev/null | grep -q "default via $GATEWAY6 dev $INTERFACE proto static.*initcwnd $INITCWND"; then
if [ -n "$IP6" ]; then
ip -6 route add default via "$GATEWAY6" dev "$INTERFACE" proto static src "$IP6" \
metric 500 quickack 1 initcwnd "$INITCWND" initrwnd "$INITRWND" pref high 2>/dev/null || true
else
ip -6 route add default via "$GATEWAY6" dev "$INTERFACE" proto static \
metric 500 quickack 1 initcwnd "$INITCWND" initrwnd "$INITRWND" pref high 2>/dev/null || true
fi
fi
fi
fi
fi
exit 0