Skip to content

Commit a95c0f4

Browse files
committed
Added --no-mtu flag to disable setting MTU 9000 on Ethernet NICs
1 parent 5f12a4a commit a95c0f4

File tree

1 file changed

+94
-116
lines changed

1 file changed

+94
-116
lines changed
Lines changed: 94 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,34 @@
1-
setup-multi-nic-routing.sh#!/usr/bin/env bash
1+
#!/usr/bin/env bash
22
#
33
# setup-multi-nic-routing.sh
4-
# v1.0
54
#
6-
# Configures source-based routing for multiple NICs using NetworkManager.
5+
# This script configures source-based routing for multiple NICs using NetworkManager.
76
#
8-
# Features:
9-
# - Validates NICs are present and up
10-
# - Creates dedicated routing tables: weka-<nic>
11-
# - Adds source-based routing rules
12-
# - Routes local subnet and optional client subnet via NIC
13-
# - Enables ARP tuning (arp_filter/arp_announce)
14-
# - Sets MTU=9000 for Ethernet interfaces
15-
# - --reset: removes rules/routes only, preserves NM connections
16-
# - Supports multi-run, multi-NIC, different subnets safely
7+
# For each specified NIC:
8+
# - Validates the interface is present and up
9+
# - Retrieves its IPv4 address and subnet
10+
# - Creates a dedicated routing table (e.g. weka-eth0, weka-eth1, ...)
11+
# - Adds a source-based routing rule for traffic from that IP
12+
# - Adds a route to the local subnet via the same interface and IP
13+
# - Optionally adds a route to a remote client subnet via a specified gateway
14+
# - Enables ARP tuning (arp_filter=1, arp_announce=2) only for the listed NICs
15+
# - Sets MTU=9000 for Ethernet interfaces (unless --no-mtu is used)
1716
#
18-
# Usage examples:
19-
# ./setup-multi-nic-routing.sh --nics ib0 ib1
17+
# Supports:
18+
# --nics Space-separated list of NICs (required)
19+
# --client-subnet Optional remote subnet (CIDR format) to route via gateway (could be 0.0.0.0/0)
20+
# --gateway Optional gateway for the client subnet
21+
# --reset Removes any existing source-based routing rules created by this script
22+
# --no-mtu Skip setting MTU=9000 for Ethernet NICs
23+
# --dry-run Show intended actions without applying them
24+
#
25+
# Example usage:
2026
# ./setup-multi-nic-routing.sh --nics eth0 eth1 --client-subnet 192.168.50.0/24 --gateway 10.0.0.254
21-
# ./setup-multi-nic-routing.sh --nics ens19 ens20 --reset
22-
# ./setup-multi-nic-routing.sh --nics eth0 --client-subnet 0.0.0.0/0 --gateway 10.1.1.254
23-
# ./setup-multi-nic-routing.sh --nics eth1 --client-subnet 0.0.0.0/0 --gateway 10.1.2.254
27+
# ./setup-multi-nic-routing.sh --nics eth0 eth1 --client-subnet 0.0.0.0/0 --gateway 10.1.1.254
28+
# ./setup-multi-nic-routing.sh --nics ib0 ib1 # Flat IB or Ethernet (no gateway)
29+
# ./setup-multi-nic-routing.sh --nics eth0 eth1 --reset # Remove only routing rules
30+
# ./setup-multi-nic-routing.sh --nics eth0 eth1 --no-mtu # Skip MTU 9000 change
31+
# ./setup-multi-nic-routing.sh --nics eth0 eth1 --dry-run # Preview only
2432
#
2533

2634
set -euo pipefail
@@ -30,12 +38,13 @@ ROUTE_TABLE_PREFIX="weka"
3038
SYSCTL_FILE="/etc/sysctl.d/99-weka.conf"
3139
RT_TABLES="/etc/iproute2/rt_tables"
3240
DRY_RUN=false
33-
RESET=false
41+
RESET_MODE=false
42+
NO_MTU=false
3443

3544
# ---- FUNCTIONS ----
3645

3746
usage() {
38-
echo "Usage: $0 --nics <nic1> [nic2 ...] [--client-subnet <CIDR>] [--gateway <IP>] [--dry-run] [--reset]"
47+
echo "Usage: $0 --nics <nic1> [nic2 ...] [--client-subnet <CIDR>] [--gateway <IP>] [--reset] [--no-mtu] [--dry-run]"
3948
exit 1
4049
}
4150

@@ -69,9 +78,15 @@ get_ip_for_nic() {
6978

7079
get_network_cidr() {
7180
local ip_cidr="$1"
72-
local network_line
73-
network_line=$(ipcalc "$ip_cidr" 2>/dev/null | awk '/^Network:/ {print $2}')
74-
echo "$network_line"
81+
ipcalc "$ip_cidr" 2>/dev/null | awk '/^Network:/ {print $2}'
82+
}
83+
84+
ensure_rt_table() {
85+
local table_id="$1"
86+
local table_name="$2"
87+
if ! grep -qw "$table_name" "$RT_TABLES"; then
88+
run_or_echo "echo \"$table_id $table_name\" >> \"$RT_TABLES\""
89+
fi
7590
}
7691

7792
apply_sysctl_arp_settings() {
@@ -91,55 +106,27 @@ apply_sysctl_arp_settings() {
91106
fi
92107
}
93108

94-
# Reset rules only (do NOT delete NM connection)
95-
reset_nic_config() {
109+
find_existing_table_id() {
96110
local nic="$1"
97-
local table_name="${ROUTE_TABLE_PREFIX}-${nic}"
98-
99-
echo "🧹 Resetting routing rules for $nic ..."
100-
101-
# Remove matching ip rules
102-
local rules
103-
rules=$(ip rule show | grep -w "lookup" | grep "$table_name" || true)
104-
if [[ -n "$rules" ]]; then
105-
echo "$rules" | while read -r rule; do
106-
local priority
107-
priority=$(echo "$rule" | awk '{print $1}' | sed 's/://')
108-
run_or_echo "ip rule del priority $priority"
109-
done
110-
else
111-
echo " No ip rules found for $table_name"
112-
fi
113-
114-
# Flush the routing table
115-
run_or_echo "ip route flush table $table_name || true"
116-
117-
echo " ✅ Routing rules cleared for $nic"
111+
grep -E "weka-${nic}" "$RT_TABLES" | awk '{print $1}' | head -n1
118112
}
119113

120-
# Get or allocate a routing table ID (ensure unique)
121-
get_or_allocate_table_id() {
122-
local nic="$1"
123-
local table_name="${ROUTE_TABLE_PREFIX}-${nic}"
124-
125-
# Reuse table if it exists
126-
local existing_entry
127-
existing_entry=$(grep -w "$table_name" "$RT_TABLES" | awk '{print $1}' || true)
128-
if [[ -n "$existing_entry" ]]; then
129-
echo "$existing_entry"
130-
return
131-
fi
114+
find_next_table_id() {
115+
awk '$1 ~ /^[0-9]+$/ {print $1}' "$RT_TABLES" | sort -n | tail -1 | awk '{print $1+1}'
116+
}
132117

133-
# Allocate next free numeric ID starting from 100
134-
local used_ids
135-
used_ids=$(awk '{print $1}' "$RT_TABLES" | grep -E '^[0-9]+$' | sort -n | uniq)
136-
local id=100
137-
while echo "$used_ids" | grep -qw "$id"; do
138-
((id++))
118+
reset_routing_rules() {
119+
for nic in "${NIC_LIST[@]}"; do
120+
echo "🧹 Resetting rules for $nic..."
121+
local ip_spec
122+
ip_spec=$(get_ip_for_nic "$nic")
123+
local ip_only="${ip_spec%%/*}"
124+
ip rule show | grep -w "$ip_only" | awk -F: '{print $1}' | while read -r rule_id; do
125+
run_or_echo "ip rule del pref $rule_id"
126+
done
139127
done
140-
141-
run_or_echo "echo \"$id $table_name\" >> \"$RT_TABLES\""
142-
echo "$id"
128+
echo "✅ Reset complete — existing routing rules removed (interfaces untouched)."
129+
exit 0
143130
}
144131

145132
configure_nic() {
@@ -153,46 +140,39 @@ configure_nic() {
153140
local local_subnet
154141
local_subnet=$(get_network_cidr "$ip_spec")
155142
local ip_only="${ip_spec%%/*}"
156-
local con_name="${nic}"
157143
local nic_type
158144
nic_type=$(nmcli -g GENERAL.TYPE device show "$nic" 2>/dev/null | head -n1)
159145

160146
echo "⚙️ Configuring $nic (Type: $nic_type, IP: $ip_only, Local subnet: $local_subnet, Table: $table_name)..."
161147

162-
run_or_echo "nmcli con delete \"$con_name\" &>/dev/null || true"
163-
164-
run_or_echo "nmcli con add type \"$nic_type\" ifname \"$nic\" con-name \"$con_name\" \
165-
ipv4.addresses \"$ip_spec\" \
166-
ipv4.method manual \
167-
connection.autoconnect yes \
168-
ipv4.route-metric 0 \
169-
ipv4.routing-rules \"priority $table_id from $ip_only table $table_id\""
170-
171-
if [[ "$nic_type" == "ethernet" ]]; then
172-
echo "📦 Setting MTU 9000 on $nic (Ethernet)"
173-
run_or_echo "nmcli con modify \"$con_name\" 802-3-ethernet.mtu 9000"
148+
# Configure routing rule
149+
if ! ip rule show | grep -q "from $ip_only lookup $table_name"; then
150+
run_or_echo "ip rule add from $ip_only table $table_name pref $table_id"
151+
else
152+
echo "ℹ️ Rule already exists for $ip_only → table $table_name"
174153
fi
175154

176-
# Add local subnet route
177-
echo "🔁 Adding route to local subnet: $local_subnet src=$ip_only table=$table_id"
178-
run_or_echo "nmcli connection modify \"$con_name\" +ipv4.routes \"$local_subnet src=$ip_only table=$table_id\""
155+
# Local subnet route
156+
run_or_echo "ip route replace $local_subnet dev $nic src $ip_only table $table_name"
179157

180-
# Add client subnet route
158+
# Optional client subnet route (skip main if 0.0.0.0/0)
181159
if [[ -n "$client_subnet" && -n "$gateway" ]]; then
182-
echo "📡 Adding route to client subnet: $client_subnet via $gateway (table: $table_name)"
183-
# Always add to NIC's custom table
184-
run_or_echo "nmcli connection modify \"$con_name\" +ipv4.routes \"$client_subnet $gateway table=$table_id\""
185-
186-
# Only add to main table if not default route
187-
if [[ "$client_subnet" != "0.0.0.0/0" ]]; then
188-
run_or_echo "nmcli connection modify \"$con_name\" +ipv4.routes \"$client_subnet $gateway\""
160+
if [[ "$client_subnet" == "0.0.0.0/0" ]]; then
161+
echo "🌍 Adding default route (client subnet) to table $table_name only"
162+
run_or_echo "ip route replace default via $gateway dev $nic table $table_name"
189163
else
190-
echo "⚠️ Skipping adding 0.0.0.0/0 to main routing table; route exists only in $table_name"
164+
echo "📡 Adding route to client subnet: $client_subnet via $gateway"
165+
run_or_echo "ip route replace $client_subnet via $gateway dev $nic table $table_name"
191166
fi
192167
fi
193168

194-
run_or_echo "nmcli con down \"$con_name\""
195-
run_or_echo "nmcli con up \"$con_name\""
169+
# Set MTU if Ethernet and not disabled
170+
if [[ "$nic_type" == "ethernet" && $NO_MTU == false ]]; then
171+
echo "📦 Setting MTU 9000 on $nic (Ethernet)"
172+
run_or_echo "ip link set dev $nic mtu 9000"
173+
elif [[ "$nic_type" == "ethernet" && $NO_MTU == true ]]; then
174+
echo "🚫 Skipping MTU change on $nic (Ethernet) due to --no-mtu flag"
175+
fi
196176
}
197177

198178
# ---- MAIN ----
@@ -201,7 +181,6 @@ NIC_LIST=()
201181
CLIENT_SUBNET=""
202182
GATEWAY=""
203183

204-
# Parse CLI arguments
205184
while [[ $# -gt 0 ]]; do
206185
case "$1" in
207186
--nics)
@@ -219,12 +198,16 @@ while [[ $# -gt 0 ]]; do
219198
GATEWAY="$2"
220199
shift 2
221200
;;
222-
--dry-run)
223-
DRY_RUN=true
201+
--reset)
202+
RESET_MODE=true
224203
shift
225204
;;
226-
--reset)
227-
RESET=true
205+
--no-mtu)
206+
NO_MTU=true
207+
shift
208+
;;
209+
--dry-run)
210+
DRY_RUN=true
228211
shift
229212
;;
230213
*)
@@ -234,27 +217,18 @@ while [[ $# -gt 0 ]]; do
234217
esac
235218
done
236219

237-
# Validate input
238220
if [[ ${#NIC_LIST[@]} -lt 1 ]]; then
239221
errmsg "At least one NIC must be specified"
240222
usage
241223
fi
242224

243225
check_requirements
226+
apply_sysctl_arp_settings
244227

245-
# ---- RESET MODE ----
246-
if $RESET; then
247-
echo "🔄 Reset mode enabled — clearing routing rules and tables for specified NICs."
248-
for nic in "${NIC_LIST[@]}"; do
249-
reset_nic_config "$nic"
250-
done
251-
echo "✅ Reset complete. NM connections and ARP settings preserved."
252-
exit 0
228+
if $RESET_MODE; then
229+
reset_routing_rules
253230
fi
254231

255-
# ---- NORMAL CONFIGURATION ----
256-
apply_sysctl_arp_settings
257-
258232
for nic in "${NIC_LIST[@]}"; do
259233
validate_nic "$nic"
260234
ip_spec=$(get_ip_for_nic "$nic")
@@ -263,18 +237,22 @@ for nic in "${NIC_LIST[@]}"; do
263237
exit 1
264238
fi
265239

266-
table_name="${ROUTE_TABLE_PREFIX}-${nic}"
267-
table_id=$(get_or_allocate_table_id "$nic")
240+
table_id=$(find_existing_table_id "$nic" || true)
241+
if [[ -z "$table_id" ]]; then
242+
table_id=$(find_next_table_id)
243+
table_name="${ROUTE_TABLE_PREFIX}-${nic}"
244+
ensure_rt_table "$table_id" "$table_name"
245+
else
246+
table_name=$(grep -E "^[[:space:]]*$table_id" "$RT_TABLES" | awk '{print $2}')
247+
fi
268248

269249
configure_nic "$nic" "$ip_spec" "$GATEWAY" "$CLIENT_SUBNET" "$table_name" "$table_id"
270250
done
271251

272252
if ! $DRY_RUN; then
273253
echo "⚙️ Configuring NetworkManager to ignore carrier"
274-
echo "[main]" > /etc/NetworkManager/conf.d/99-weka-carrier.conf
275-
echo "ignore-carrier=*" >> /etc/NetworkManager/conf.d/99-weka-carrier.conf
254+
echo "[main]" > /etc/NetworkManager/conf.d/99-weka-carrier.conf && echo "ignore-carrier=*" >> /etc/NetworkManager/conf.d/99-weka-carrier.conf
276255
fi
277256

278-
echo "${DRY_RUN:-false}" | grep -q true && tag='[dry-run] ' || tag=''
279-
echo "${tag}Successfully processed ${#NIC_LIST[@]} NIC(s)."
257+
echo "✅ Successfully processed ${#NIC_LIST[@]} NIC(s)."
280258

0 commit comments

Comments
 (0)