From 5f12a4aa78d2fb4dd5f21220f2d1f84a984a773d Mon Sep 17 00:00:00 2001 From: adamuk01 <85671778+adamuk01@users.noreply.github.com> Date: Tue, 11 Nov 2025 11:54:29 +0000 Subject: [PATCH 1/7] Added setup-multi-nic-routing.sh - script designed to setup SBR in NetworkManager --- preinstall/setup-multi-nic-routing.sh | 280 ++++++++++++++++++++++++++ 1 file changed, 280 insertions(+) create mode 100755 preinstall/setup-multi-nic-routing.sh diff --git a/preinstall/setup-multi-nic-routing.sh b/preinstall/setup-multi-nic-routing.sh new file mode 100755 index 0000000..c5a5e81 --- /dev/null +++ b/preinstall/setup-multi-nic-routing.sh @@ -0,0 +1,280 @@ +setup-multi-nic-routing.sh#!/usr/bin/env bash +# +# setup-multi-nic-routing.sh +# v1.0 +# +# Configures source-based routing for multiple NICs using NetworkManager. +# +# Features: +# - Validates NICs are present and up +# - Creates dedicated routing tables: weka- +# - Adds source-based routing rules +# - Routes local subnet and optional client subnet via NIC +# - Enables ARP tuning (arp_filter/arp_announce) +# - Sets MTU=9000 for Ethernet interfaces +# - --reset: removes rules/routes only, preserves NM connections +# - Supports multi-run, multi-NIC, different subnets safely +# +# Usage examples: +# ./setup-multi-nic-routing.sh --nics ib0 ib1 +# ./setup-multi-nic-routing.sh --nics eth0 eth1 --client-subnet 192.168.50.0/24 --gateway 10.0.0.254 +# ./setup-multi-nic-routing.sh --nics ens19 ens20 --reset +# ./setup-multi-nic-routing.sh --nics eth0 --client-subnet 0.0.0.0/0 --gateway 10.1.1.254 +# ./setup-multi-nic-routing.sh --nics eth1 --client-subnet 0.0.0.0/0 --gateway 10.1.2.254 +# + +set -euo pipefail + +# ---- CONFIG ---- +ROUTE_TABLE_PREFIX="weka" +SYSCTL_FILE="/etc/sysctl.d/99-weka.conf" +RT_TABLES="/etc/iproute2/rt_tables" +DRY_RUN=false +RESET=false + +# ---- FUNCTIONS ---- + +usage() { + echo "Usage: $0 --nics [nic2 ...] [--client-subnet ] [--gateway ] [--dry-run] [--reset]" + exit 1 +} + +errmsg() { + echo "[ERROR] $*" >&2 +} + +run_or_echo() { + if $DRY_RUN; then + echo "[dry-run] $*" + else + eval "$@" + fi +} + +check_requirements() { + for cmd in nmcli ip ipcalc; do + command -v $cmd >/dev/null || { errmsg "$cmd not found"; exit 1; } + done +} + +validate_nic() { + local nic="$1" + ip link show "$nic" &>/dev/null || { errmsg "NIC $nic not found"; exit 1; } + ip link show up "$nic" | grep -q "$nic" || { errmsg "NIC $nic is down"; exit 1; } +} + +get_ip_for_nic() { + ip -4 addr show "$1" | awk '/inet / {print $2}' | head -n1 +} + +get_network_cidr() { + local ip_cidr="$1" + local network_line + network_line=$(ipcalc "$ip_cidr" 2>/dev/null | awk '/^Network:/ {print $2}') + echo "$network_line" +} + +apply_sysctl_arp_settings() { + echo "πŸ”§ Setting ARP tuning in $SYSCTL_FILE for interfaces: ${NIC_LIST[*]}" + if ! $DRY_RUN; then + : > "$SYSCTL_FILE" + fi + for nic in "${NIC_LIST[@]}"; do + run_or_echo "echo \"net.ipv4.conf.${nic}.arp_filter = 1\" >> \"$SYSCTL_FILE\"" + run_or_echo "echo \"net.ipv4.conf.${nic}.arp_ignore = 1\" >> \"$SYSCTL_FILE\"" + run_or_echo "echo \"net.ipv4.conf.${nic}.arp_announce = 2\" >> \"$SYSCTL_FILE\"" + done + if ! $DRY_RUN; then + sysctl --system >/dev/null + else + echo "[dry-run] sysctl --system" + fi +} + +# Reset rules only (do NOT delete NM connection) +reset_nic_config() { + local nic="$1" + local table_name="${ROUTE_TABLE_PREFIX}-${nic}" + + echo "🧹 Resetting routing rules for $nic ..." + + # Remove matching ip rules + local rules + rules=$(ip rule show | grep -w "lookup" | grep "$table_name" || true) + if [[ -n "$rules" ]]; then + echo "$rules" | while read -r rule; do + local priority + priority=$(echo "$rule" | awk '{print $1}' | sed 's/://') + run_or_echo "ip rule del priority $priority" + done + else + echo " No ip rules found for $table_name" + fi + + # Flush the routing table + run_or_echo "ip route flush table $table_name || true" + + echo " βœ… Routing rules cleared for $nic" +} + +# Get or allocate a routing table ID (ensure unique) +get_or_allocate_table_id() { + local nic="$1" + local table_name="${ROUTE_TABLE_PREFIX}-${nic}" + + # Reuse table if it exists + local existing_entry + existing_entry=$(grep -w "$table_name" "$RT_TABLES" | awk '{print $1}' || true) + if [[ -n "$existing_entry" ]]; then + echo "$existing_entry" + return + fi + + # Allocate next free numeric ID starting from 100 + local used_ids + used_ids=$(awk '{print $1}' "$RT_TABLES" | grep -E '^[0-9]+$' | sort -n | uniq) + local id=100 + while echo "$used_ids" | grep -qw "$id"; do + ((id++)) + done + + run_or_echo "echo \"$id $table_name\" >> \"$RT_TABLES\"" + echo "$id" +} + +configure_nic() { + local nic="$1" + local ip_spec="$2" + local gateway="$3" + local client_subnet="$4" + local table_name="$5" + local table_id="$6" + + local local_subnet + local_subnet=$(get_network_cidr "$ip_spec") + local ip_only="${ip_spec%%/*}" + local con_name="${nic}" + local nic_type + nic_type=$(nmcli -g GENERAL.TYPE device show "$nic" 2>/dev/null | head -n1) + + echo "βš™οΈ Configuring $nic (Type: $nic_type, IP: $ip_only, Local subnet: $local_subnet, Table: $table_name)..." + + run_or_echo "nmcli con delete \"$con_name\" &>/dev/null || true" + + run_or_echo "nmcli con add type \"$nic_type\" ifname \"$nic\" con-name \"$con_name\" \ + ipv4.addresses \"$ip_spec\" \ + ipv4.method manual \ + connection.autoconnect yes \ + ipv4.route-metric 0 \ + ipv4.routing-rules \"priority $table_id from $ip_only table $table_id\"" + + if [[ "$nic_type" == "ethernet" ]]; then + echo "πŸ“¦ Setting MTU 9000 on $nic (Ethernet)" + run_or_echo "nmcli con modify \"$con_name\" 802-3-ethernet.mtu 9000" + fi + + # Add local subnet route + echo "πŸ” Adding route to local subnet: $local_subnet src=$ip_only table=$table_id" + run_or_echo "nmcli connection modify \"$con_name\" +ipv4.routes \"$local_subnet src=$ip_only table=$table_id\"" + + # Add client subnet route + if [[ -n "$client_subnet" && -n "$gateway" ]]; then + echo "πŸ“‘ Adding route to client subnet: $client_subnet via $gateway (table: $table_name)" + # Always add to NIC's custom table + run_or_echo "nmcli connection modify \"$con_name\" +ipv4.routes \"$client_subnet $gateway table=$table_id\"" + + # Only add to main table if not default route + if [[ "$client_subnet" != "0.0.0.0/0" ]]; then + run_or_echo "nmcli connection modify \"$con_name\" +ipv4.routes \"$client_subnet $gateway\"" + else + echo "⚠️ Skipping adding 0.0.0.0/0 to main routing table; route exists only in $table_name" + fi + fi + + run_or_echo "nmcli con down \"$con_name\"" + run_or_echo "nmcli con up \"$con_name\"" +} + +# ---- MAIN ---- + +NIC_LIST=() +CLIENT_SUBNET="" +GATEWAY="" + +# Parse CLI arguments +while [[ $# -gt 0 ]]; do + case "$1" in + --nics) + shift + while [[ $# -gt 0 && "$1" != --* ]]; do + NIC_LIST+=("$1") + shift + done + ;; + --client-subnet) + CLIENT_SUBNET="$2" + shift 2 + ;; + --gateway) + GATEWAY="$2" + shift 2 + ;; + --dry-run) + DRY_RUN=true + shift + ;; + --reset) + RESET=true + shift + ;; + *) + errmsg "Unknown argument: $1" + usage + ;; + esac +done + +# Validate input +if [[ ${#NIC_LIST[@]} -lt 1 ]]; then + errmsg "At least one NIC must be specified" + usage +fi + +check_requirements + +# ---- RESET MODE ---- +if $RESET; then + echo "πŸ”„ Reset mode enabled β€” clearing routing rules and tables for specified NICs." + for nic in "${NIC_LIST[@]}"; do + reset_nic_config "$nic" + done + echo "βœ… Reset complete. NM connections and ARP settings preserved." + exit 0 +fi + +# ---- NORMAL CONFIGURATION ---- +apply_sysctl_arp_settings + +for nic in "${NIC_LIST[@]}"; do + validate_nic "$nic" + ip_spec=$(get_ip_for_nic "$nic") + if [[ -z "$ip_spec" ]]; then + errmsg "No IP address found on NIC $nic" + exit 1 + fi + + table_name="${ROUTE_TABLE_PREFIX}-${nic}" + table_id=$(get_or_allocate_table_id "$nic") + + configure_nic "$nic" "$ip_spec" "$GATEWAY" "$CLIENT_SUBNET" "$table_name" "$table_id" +done + +if ! $DRY_RUN; then + echo "βš™οΈ Configuring NetworkManager to ignore carrier" + echo "[main]" > /etc/NetworkManager/conf.d/99-weka-carrier.conf + echo "ignore-carrier=*" >> /etc/NetworkManager/conf.d/99-weka-carrier.conf +fi + +echo "βœ… ${DRY_RUN:-false}" | grep -q true && tag='[dry-run] ' || tag='' +echo "βœ… ${tag}Successfully processed ${#NIC_LIST[@]} NIC(s)." + From a95c0f4870c22a223c6c5d8ac0d66e44242b3b6a Mon Sep 17 00:00:00 2001 From: adamuk01 <85671778+adamuk01@users.noreply.github.com> Date: Wed, 12 Nov 2025 09:56:19 +0000 Subject: [PATCH 2/7] Added --no-mtu flag to disable setting MTU 9000 on Ethernet NICs --- preinstall/setup-multi-nic-routing.sh | 210 ++++++++++++-------------- 1 file changed, 94 insertions(+), 116 deletions(-) diff --git a/preinstall/setup-multi-nic-routing.sh b/preinstall/setup-multi-nic-routing.sh index c5a5e81..3dec747 100755 --- a/preinstall/setup-multi-nic-routing.sh +++ b/preinstall/setup-multi-nic-routing.sh @@ -1,26 +1,34 @@ -setup-multi-nic-routing.sh#!/usr/bin/env bash +#!/usr/bin/env bash # # setup-multi-nic-routing.sh -# v1.0 # -# Configures source-based routing for multiple NICs using NetworkManager. +# This script configures source-based routing for multiple NICs using NetworkManager. # -# Features: -# - Validates NICs are present and up -# - Creates dedicated routing tables: weka- -# - Adds source-based routing rules -# - Routes local subnet and optional client subnet via NIC -# - Enables ARP tuning (arp_filter/arp_announce) -# - Sets MTU=9000 for Ethernet interfaces -# - --reset: removes rules/routes only, preserves NM connections -# - Supports multi-run, multi-NIC, different subnets safely +# For each specified NIC: +# - Validates the interface is present and up +# - Retrieves its IPv4 address and subnet +# - Creates a dedicated routing table (e.g. weka-eth0, weka-eth1, ...) +# - Adds a source-based routing rule for traffic from that IP +# - Adds a route to the local subnet via the same interface and IP +# - Optionally adds a route to a remote client subnet via a specified gateway +# - Enables ARP tuning (arp_filter=1, arp_announce=2) only for the listed NICs +# - Sets MTU=9000 for Ethernet interfaces (unless --no-mtu is used) # -# Usage examples: -# ./setup-multi-nic-routing.sh --nics ib0 ib1 +# Supports: +# --nics Space-separated list of NICs (required) +# --client-subnet Optional remote subnet (CIDR format) to route via gateway (could be 0.0.0.0/0) +# --gateway Optional gateway for the client subnet +# --reset Removes any existing source-based routing rules created by this script +# --no-mtu Skip setting MTU=9000 for Ethernet NICs +# --dry-run Show intended actions without applying them +# +# Example usage: # ./setup-multi-nic-routing.sh --nics eth0 eth1 --client-subnet 192.168.50.0/24 --gateway 10.0.0.254 -# ./setup-multi-nic-routing.sh --nics ens19 ens20 --reset -# ./setup-multi-nic-routing.sh --nics eth0 --client-subnet 0.0.0.0/0 --gateway 10.1.1.254 -# ./setup-multi-nic-routing.sh --nics eth1 --client-subnet 0.0.0.0/0 --gateway 10.1.2.254 +# ./setup-multi-nic-routing.sh --nics eth0 eth1 --client-subnet 0.0.0.0/0 --gateway 10.1.1.254 +# ./setup-multi-nic-routing.sh --nics ib0 ib1 # Flat IB or Ethernet (no gateway) +# ./setup-multi-nic-routing.sh --nics eth0 eth1 --reset # Remove only routing rules +# ./setup-multi-nic-routing.sh --nics eth0 eth1 --no-mtu # Skip MTU 9000 change +# ./setup-multi-nic-routing.sh --nics eth0 eth1 --dry-run # Preview only # set -euo pipefail @@ -30,12 +38,13 @@ ROUTE_TABLE_PREFIX="weka" SYSCTL_FILE="/etc/sysctl.d/99-weka.conf" RT_TABLES="/etc/iproute2/rt_tables" DRY_RUN=false -RESET=false +RESET_MODE=false +NO_MTU=false # ---- FUNCTIONS ---- usage() { - echo "Usage: $0 --nics [nic2 ...] [--client-subnet ] [--gateway ] [--dry-run] [--reset]" + echo "Usage: $0 --nics [nic2 ...] [--client-subnet ] [--gateway ] [--reset] [--no-mtu] [--dry-run]" exit 1 } @@ -69,9 +78,15 @@ get_ip_for_nic() { get_network_cidr() { local ip_cidr="$1" - local network_line - network_line=$(ipcalc "$ip_cidr" 2>/dev/null | awk '/^Network:/ {print $2}') - echo "$network_line" + ipcalc "$ip_cidr" 2>/dev/null | awk '/^Network:/ {print $2}' +} + +ensure_rt_table() { + local table_id="$1" + local table_name="$2" + if ! grep -qw "$table_name" "$RT_TABLES"; then + run_or_echo "echo \"$table_id $table_name\" >> \"$RT_TABLES\"" + fi } apply_sysctl_arp_settings() { @@ -91,55 +106,27 @@ apply_sysctl_arp_settings() { fi } -# Reset rules only (do NOT delete NM connection) -reset_nic_config() { +find_existing_table_id() { local nic="$1" - local table_name="${ROUTE_TABLE_PREFIX}-${nic}" - - echo "🧹 Resetting routing rules for $nic ..." - - # Remove matching ip rules - local rules - rules=$(ip rule show | grep -w "lookup" | grep "$table_name" || true) - if [[ -n "$rules" ]]; then - echo "$rules" | while read -r rule; do - local priority - priority=$(echo "$rule" | awk '{print $1}' | sed 's/://') - run_or_echo "ip rule del priority $priority" - done - else - echo " No ip rules found for $table_name" - fi - - # Flush the routing table - run_or_echo "ip route flush table $table_name || true" - - echo " βœ… Routing rules cleared for $nic" + grep -E "weka-${nic}" "$RT_TABLES" | awk '{print $1}' | head -n1 } -# Get or allocate a routing table ID (ensure unique) -get_or_allocate_table_id() { - local nic="$1" - local table_name="${ROUTE_TABLE_PREFIX}-${nic}" - - # Reuse table if it exists - local existing_entry - existing_entry=$(grep -w "$table_name" "$RT_TABLES" | awk '{print $1}' || true) - if [[ -n "$existing_entry" ]]; then - echo "$existing_entry" - return - fi +find_next_table_id() { + awk '$1 ~ /^[0-9]+$/ {print $1}' "$RT_TABLES" | sort -n | tail -1 | awk '{print $1+1}' +} - # Allocate next free numeric ID starting from 100 - local used_ids - used_ids=$(awk '{print $1}' "$RT_TABLES" | grep -E '^[0-9]+$' | sort -n | uniq) - local id=100 - while echo "$used_ids" | grep -qw "$id"; do - ((id++)) +reset_routing_rules() { + for nic in "${NIC_LIST[@]}"; do + echo "🧹 Resetting rules for $nic..." + local ip_spec + ip_spec=$(get_ip_for_nic "$nic") + local ip_only="${ip_spec%%/*}" + ip rule show | grep -w "$ip_only" | awk -F: '{print $1}' | while read -r rule_id; do + run_or_echo "ip rule del pref $rule_id" + done done - - run_or_echo "echo \"$id $table_name\" >> \"$RT_TABLES\"" - echo "$id" + echo "βœ… Reset complete β€” existing routing rules removed (interfaces untouched)." + exit 0 } configure_nic() { @@ -153,46 +140,39 @@ configure_nic() { local local_subnet local_subnet=$(get_network_cidr "$ip_spec") local ip_only="${ip_spec%%/*}" - local con_name="${nic}" local nic_type nic_type=$(nmcli -g GENERAL.TYPE device show "$nic" 2>/dev/null | head -n1) echo "βš™οΈ Configuring $nic (Type: $nic_type, IP: $ip_only, Local subnet: $local_subnet, Table: $table_name)..." - run_or_echo "nmcli con delete \"$con_name\" &>/dev/null || true" - - run_or_echo "nmcli con add type \"$nic_type\" ifname \"$nic\" con-name \"$con_name\" \ - ipv4.addresses \"$ip_spec\" \ - ipv4.method manual \ - connection.autoconnect yes \ - ipv4.route-metric 0 \ - ipv4.routing-rules \"priority $table_id from $ip_only table $table_id\"" - - if [[ "$nic_type" == "ethernet" ]]; then - echo "πŸ“¦ Setting MTU 9000 on $nic (Ethernet)" - run_or_echo "nmcli con modify \"$con_name\" 802-3-ethernet.mtu 9000" + # Configure routing rule + if ! ip rule show | grep -q "from $ip_only lookup $table_name"; then + run_or_echo "ip rule add from $ip_only table $table_name pref $table_id" + else + echo "ℹ️ Rule already exists for $ip_only β†’ table $table_name" fi - # Add local subnet route - echo "πŸ” Adding route to local subnet: $local_subnet src=$ip_only table=$table_id" - run_or_echo "nmcli connection modify \"$con_name\" +ipv4.routes \"$local_subnet src=$ip_only table=$table_id\"" + # Local subnet route + run_or_echo "ip route replace $local_subnet dev $nic src $ip_only table $table_name" - # Add client subnet route + # Optional client subnet route (skip main if 0.0.0.0/0) if [[ -n "$client_subnet" && -n "$gateway" ]]; then - echo "πŸ“‘ Adding route to client subnet: $client_subnet via $gateway (table: $table_name)" - # Always add to NIC's custom table - run_or_echo "nmcli connection modify \"$con_name\" +ipv4.routes \"$client_subnet $gateway table=$table_id\"" - - # Only add to main table if not default route - if [[ "$client_subnet" != "0.0.0.0/0" ]]; then - run_or_echo "nmcli connection modify \"$con_name\" +ipv4.routes \"$client_subnet $gateway\"" + if [[ "$client_subnet" == "0.0.0.0/0" ]]; then + echo "🌍 Adding default route (client subnet) to table $table_name only" + run_or_echo "ip route replace default via $gateway dev $nic table $table_name" else - echo "⚠️ Skipping adding 0.0.0.0/0 to main routing table; route exists only in $table_name" + echo "πŸ“‘ Adding route to client subnet: $client_subnet via $gateway" + run_or_echo "ip route replace $client_subnet via $gateway dev $nic table $table_name" fi fi - run_or_echo "nmcli con down \"$con_name\"" - run_or_echo "nmcli con up \"$con_name\"" + # Set MTU if Ethernet and not disabled + if [[ "$nic_type" == "ethernet" && $NO_MTU == false ]]; then + echo "πŸ“¦ Setting MTU 9000 on $nic (Ethernet)" + run_or_echo "ip link set dev $nic mtu 9000" + elif [[ "$nic_type" == "ethernet" && $NO_MTU == true ]]; then + echo "🚫 Skipping MTU change on $nic (Ethernet) due to --no-mtu flag" + fi } # ---- MAIN ---- @@ -201,7 +181,6 @@ NIC_LIST=() CLIENT_SUBNET="" GATEWAY="" -# Parse CLI arguments while [[ $# -gt 0 ]]; do case "$1" in --nics) @@ -219,12 +198,16 @@ while [[ $# -gt 0 ]]; do GATEWAY="$2" shift 2 ;; - --dry-run) - DRY_RUN=true + --reset) + RESET_MODE=true shift ;; - --reset) - RESET=true + --no-mtu) + NO_MTU=true + shift + ;; + --dry-run) + DRY_RUN=true shift ;; *) @@ -234,27 +217,18 @@ while [[ $# -gt 0 ]]; do esac done -# Validate input if [[ ${#NIC_LIST[@]} -lt 1 ]]; then errmsg "At least one NIC must be specified" usage fi check_requirements +apply_sysctl_arp_settings -# ---- RESET MODE ---- -if $RESET; then - echo "πŸ”„ Reset mode enabled β€” clearing routing rules and tables for specified NICs." - for nic in "${NIC_LIST[@]}"; do - reset_nic_config "$nic" - done - echo "βœ… Reset complete. NM connections and ARP settings preserved." - exit 0 +if $RESET_MODE; then + reset_routing_rules fi -# ---- NORMAL CONFIGURATION ---- -apply_sysctl_arp_settings - for nic in "${NIC_LIST[@]}"; do validate_nic "$nic" ip_spec=$(get_ip_for_nic "$nic") @@ -263,18 +237,22 @@ for nic in "${NIC_LIST[@]}"; do exit 1 fi - table_name="${ROUTE_TABLE_PREFIX}-${nic}" - table_id=$(get_or_allocate_table_id "$nic") + table_id=$(find_existing_table_id "$nic" || true) + if [[ -z "$table_id" ]]; then + table_id=$(find_next_table_id) + table_name="${ROUTE_TABLE_PREFIX}-${nic}" + ensure_rt_table "$table_id" "$table_name" + else + table_name=$(grep -E "^[[:space:]]*$table_id" "$RT_TABLES" | awk '{print $2}') + fi configure_nic "$nic" "$ip_spec" "$GATEWAY" "$CLIENT_SUBNET" "$table_name" "$table_id" done if ! $DRY_RUN; then echo "βš™οΈ Configuring NetworkManager to ignore carrier" - echo "[main]" > /etc/NetworkManager/conf.d/99-weka-carrier.conf - echo "ignore-carrier=*" >> /etc/NetworkManager/conf.d/99-weka-carrier.conf + echo "[main]" > /etc/NetworkManager/conf.d/99-weka-carrier.conf && echo "ignore-carrier=*" >> /etc/NetworkManager/conf.d/99-weka-carrier.conf fi -echo "βœ… ${DRY_RUN:-false}" | grep -q true && tag='[dry-run] ' || tag='' -echo "βœ… ${tag}Successfully processed ${#NIC_LIST[@]} NIC(s)." +echo "βœ… Successfully processed ${#NIC_LIST[@]} NIC(s)." From d287e65a499d33bef1136ff3934a05d2bee92795 Mon Sep 17 00:00:00 2001 From: adamuk01 <85671778+adamuk01@users.noreply.github.com> Date: Wed, 12 Nov 2025 11:37:10 +0000 Subject: [PATCH 3/7] Fixed issue with rule not presisting. And src missing from route --- preinstall/setup-multi-nic-routing.sh | 119 ++++++++++++-------------- 1 file changed, 53 insertions(+), 66 deletions(-) diff --git a/preinstall/setup-multi-nic-routing.sh b/preinstall/setup-multi-nic-routing.sh index 3dec747..6e4aa68 100755 --- a/preinstall/setup-multi-nic-routing.sh +++ b/preinstall/setup-multi-nic-routing.sh @@ -2,38 +2,19 @@ # # setup-multi-nic-routing.sh # -# This script configures source-based routing for multiple NICs using NetworkManager. -# -# For each specified NIC: -# - Validates the interface is present and up -# - Retrieves its IPv4 address and subnet -# - Creates a dedicated routing table (e.g. weka-eth0, weka-eth1, ...) -# - Adds a source-based routing rule for traffic from that IP -# - Adds a route to the local subnet via the same interface and IP -# - Optionally adds a route to a remote client subnet via a specified gateway -# - Enables ARP tuning (arp_filter=1, arp_announce=2) only for the listed NICs -# - Sets MTU=9000 for Ethernet interfaces (unless --no-mtu is used) +# Configures source-based routing for multiple NICs using NetworkManager. +# Routes are persistent via NM. Handles local subnets and client/default routes. # # Supports: # --nics Space-separated list of NICs (required) -# --client-subnet Optional remote subnet (CIDR format) to route via gateway (could be 0.0.0.0/0) +# --client-subnet Optional remote subnet (CIDR) to route via gateway (can be 0.0.0.0/0) # --gateway Optional gateway for the client subnet -# --reset Removes any existing source-based routing rules created by this script -# --no-mtu Skip setting MTU=9000 for Ethernet NICs +# --reset Remove any existing routing rules created by this script +# --no-mtu Skip setting MTU=9000 on Ethernet NICs # --dry-run Show intended actions without applying them -# -# Example usage: -# ./setup-multi-nic-routing.sh --nics eth0 eth1 --client-subnet 192.168.50.0/24 --gateway 10.0.0.254 -# ./setup-multi-nic-routing.sh --nics eth0 eth1 --client-subnet 0.0.0.0/0 --gateway 10.1.1.254 -# ./setup-multi-nic-routing.sh --nics ib0 ib1 # Flat IB or Ethernet (no gateway) -# ./setup-multi-nic-routing.sh --nics eth0 eth1 --reset # Remove only routing rules -# ./setup-multi-nic-routing.sh --nics eth0 eth1 --no-mtu # Skip MTU 9000 change -# ./setup-multi-nic-routing.sh --nics eth0 eth1 --dry-run # Preview only -# set -euo pipefail -# ---- CONFIG ---- ROUTE_TABLE_PREFIX="weka" SYSCTL_FILE="/etc/sysctl.d/99-weka.conf" RT_TABLES="/etc/iproute2/rt_tables" @@ -41,8 +22,6 @@ DRY_RUN=false RESET_MODE=false NO_MTU=false -# ---- FUNCTIONS ---- - usage() { echo "Usage: $0 --nics [nic2 ...] [--client-subnet ] [--gateway ] [--reset] [--no-mtu] [--dry-run]" exit 1 @@ -77,8 +56,15 @@ get_ip_for_nic() { } get_network_cidr() { - local ip_cidr="$1" - ipcalc "$ip_cidr" 2>/dev/null | awk '/^Network:/ {print $2}' + local nic="$1" + local subnet + subnet=$(ip -o -f inet route show dev "$nic" scope link | awk '{print $1}' | head -n1) + if [[ -z "$subnet" ]]; then + echo "⚠️ Could not determine network CIDR for $nic, defaulting to /24" + ip_only=$(get_ip_for_nic "$nic") + subnet="${ip_only%/*}/24" + fi + echo "$subnet" } ensure_rt_table() { @@ -91,19 +77,13 @@ ensure_rt_table() { apply_sysctl_arp_settings() { echo "πŸ”§ Setting ARP tuning in $SYSCTL_FILE for interfaces: ${NIC_LIST[*]}" - if ! $DRY_RUN; then - : > "$SYSCTL_FILE" - fi + if ! $DRY_RUN; then : > "$SYSCTL_FILE"; fi for nic in "${NIC_LIST[@]}"; do run_or_echo "echo \"net.ipv4.conf.${nic}.arp_filter = 1\" >> \"$SYSCTL_FILE\"" run_or_echo "echo \"net.ipv4.conf.${nic}.arp_ignore = 1\" >> \"$SYSCTL_FILE\"" run_or_echo "echo \"net.ipv4.conf.${nic}.arp_announce = 2\" >> \"$SYSCTL_FILE\"" done - if ! $DRY_RUN; then - sysctl --system >/dev/null - else - echo "[dry-run] sysctl --system" - fi + if ! $DRY_RUN; then sysctl --system >/dev/null; else echo "[dry-run] sysctl --system"; fi } find_existing_table_id() { @@ -112,20 +92,30 @@ find_existing_table_id() { } find_next_table_id() { - awk '$1 ~ /^[0-9]+$/ {print $1}' "$RT_TABLES" | sort -n | tail -1 | awk '{print $1+1}' + local id=100 + while grep -q "^$id " "$RT_TABLES"; do ((id++)); done + echo "$id" } reset_routing_rules() { for nic in "${NIC_LIST[@]}"; do - echo "🧹 Resetting rules for $nic..." - local ip_spec - ip_spec=$(get_ip_for_nic "$nic") - local ip_only="${ip_spec%%/*}" - ip rule show | grep -w "$ip_only" | awk -F: '{print $1}' | while read -r rule_id; do - run_or_echo "ip rule del pref $rule_id" + echo "πŸ”„ Reset mode β€” clearing routing rules and persistent NM routes for $nic..." + + # Remove ip rules for this NIC + ip rule show | grep "weka-${nic}" | while read -r line; do + prio=$(echo "$line" | awk -F: '{print $1}') + [[ -n "$prio" ]] && run_or_echo "ip rule del pref $prio" done + + # Remove persistent NM routes + if nmcli -t -f NAME connection show | grep -q "^$nic$"; then + echo "🧹 Clearing persistent routes for $nic" + run_or_echo "nmcli connection modify $nic ipv4.routes '' ipv4.routing-rules ''" + run_or_echo "nmcli connection up $nic" + fi + + echo "βœ… Reset complete for $nic" done - echo "βœ… Reset complete β€” existing routing rules removed (interfaces untouched)." exit 0 } @@ -137,41 +127,38 @@ configure_nic() { local table_name="$5" local table_id="$6" - local local_subnet - local_subnet=$(get_network_cidr "$ip_spec") local ip_only="${ip_spec%%/*}" + local local_subnet + local_subnet=$(get_network_cidr "$nic") local nic_type nic_type=$(nmcli -g GENERAL.TYPE device show "$nic" 2>/dev/null | head -n1) - echo "βš™οΈ Configuring $nic (Type: $nic_type, IP: $ip_only, Local subnet: $local_subnet, Table: $table_name)..." + echo "βš™οΈ Configuring $nic (Type: $nic_type, IP: $ip_only, Local subnet: $local_subnet, Table: $table_name)..." - # Configure routing rule - if ! ip rule show | grep -q "from $ip_only lookup $table_name"; then - run_or_echo "ip rule add from $ip_only table $table_name pref $table_id" - else - echo "ℹ️ Rule already exists for $ip_only β†’ table $table_name" - fi + # Add source-based routing rule + run_or_echo "nmcli connection modify $nic ipv4.routing-rules \"priority $table_id from $ip_only table $table_id\"" - # Local subnet route - run_or_echo "ip route replace $local_subnet dev $nic src $ip_only table $table_name" + # Local subnet route (direct route, no gateway) + run_or_echo "nmcli connection modify $nic +ipv4.routes \"$local_subnet src=$ip_only table=$table_id\"" - # Optional client subnet route (skip main if 0.0.0.0/0) + # Client subnet / default route (via gateway) if [[ -n "$client_subnet" && -n "$gateway" ]]; then - if [[ "$client_subnet" == "0.0.0.0/0" ]]; then - echo "🌍 Adding default route (client subnet) to table $table_name only" - run_or_echo "ip route replace default via $gateway dev $nic table $table_name" - else - echo "πŸ“‘ Adding route to client subnet: $client_subnet via $gateway" - run_or_echo "ip route replace $client_subnet via $gateway dev $nic table $table_name" - fi + echo "🌍 Adding client subnet route via gateway $gateway" + run_or_echo "nmcli connection modify $nic +ipv4.routes \"$client_subnet $gateway table=$table_id\"" fi + # Route metric + run_or_echo "nmcli connection modify $nic ipv4.route-metric 0" + + # Bring connection up + run_or_echo "nmcli connection up $nic" + # Set MTU if Ethernet and not disabled if [[ "$nic_type" == "ethernet" && $NO_MTU == false ]]; then - echo "πŸ“¦ Setting MTU 9000 on $nic (Ethernet)" + echo "πŸ“¦ Setting MTU 9000 on $nic" run_or_echo "ip link set dev $nic mtu 9000" elif [[ "$nic_type" == "ethernet" && $NO_MTU == true ]]; then - echo "🚫 Skipping MTU change on $nic (Ethernet) due to --no-mtu flag" + echo "🚫 Skipping MTU change due to --no-mtu flag" fi } @@ -250,7 +237,7 @@ for nic in "${NIC_LIST[@]}"; do done if ! $DRY_RUN; then - echo "βš™οΈ Configuring NetworkManager to ignore carrier" + echo "βš™οΈ Configuring NetworkManager to ignore carrier" echo "[main]" > /etc/NetworkManager/conf.d/99-weka-carrier.conf && echo "ignore-carrier=*" >> /etc/NetworkManager/conf.d/99-weka-carrier.conf fi From 2f2c40a23cb46bb36948ceb68e25c0846dca869b Mon Sep 17 00:00:00 2001 From: adamuk01 <85671778+adamuk01@users.noreply.github.com> Date: Wed, 12 Nov 2025 12:41:26 +0000 Subject: [PATCH 4/7] Add netplan generator script with SBR, ARP sysctl, and optional --no-mtu --- preinstall/generate-netplan.sh | 150 +++++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100755 preinstall/generate-netplan.sh diff --git a/preinstall/generate-netplan.sh b/preinstall/generate-netplan.sh new file mode 100755 index 0000000..718688b --- /dev/null +++ b/preinstall/generate-netplan.sh @@ -0,0 +1,150 @@ +#!/usr/bin/env bash +# +# generate-netplan.sh +# +# Generate a netplan YAML file for multiple NICs with source-based routing. +# Local subnets are link-scoped (no via), client/default routes use via/gateway. +# Also generates sysctl file for ARP tuning and NUMA balancing. +# Does not apply netplan or sysctl automatically; admin must review before applying. +# The NICs might be defined in other files or separately + +set -euo pipefail + +OUTPUT_FILE="99-weka-netplan.yaml" +SYSCTL_FILE="99-weka-sysctl.conf" +NIC_LIST=() +CLIENT_SUBNET="" +GATEWAY="" +START_TABLE=101 +MTU=9000 +NO_MTU=false +APPLY_SYSCTL=false +RENDERER="networkd" + +usage() { + echo "Usage: $0 --nics [nic2 ...] [--client-subnet ] [--gateway ]" + echo " [--no-mtu] [--renderer networkd|NetworkManager] [--apply-sysctl]" + exit 1 +} + +errmsg() { echo "[ERROR] $*" >&2; } + +# Parse CLI +while [[ $# -gt 0 ]]; do + case "$1" in + --nics) + shift + while [[ $# -gt 0 && "$1" != --* ]]; do + NIC_LIST+=("$1") + shift + done + ;; + --client-subnet) + CLIENT_SUBNET="$2" + shift 2 + ;; + --gateway) + GATEWAY="$2" + shift 2 + ;; + --no-mtu) + NO_MTU=true + shift + ;; + --renderer) + RENDERER="$2" + shift 2 + ;; + --apply-sysctl) + APPLY_SYSCTL=true + shift + ;; + *) + errmsg "Unknown argument: $1" + usage + ;; + esac +done + +[[ ${#NIC_LIST[@]} -lt 1 ]] && { errmsg "At least one NIC must be specified"; usage; } + +get_ip_for_nic() { ip -4 addr show "$1" | awk '/inet / {print $2}' | head -n1; } + +get_local_subnet() { + local nic="$1" + subnet=$(ip -o -f inet route show dev "$nic" scope link | awk '{print $1}' | head -n1) + if [[ -z "$subnet" ]]; then + ip_only=$(get_ip_for_nic "$nic") + subnet="${ip_only%/*}/24" + echo "⚠️ Could not determine network CIDR for $nic, defaulting to $subnet" + fi + echo "$subnet" +} + +# Auto-add /mask if missing for client subnet +if [[ -n "$CLIENT_SUBNET" && "$CLIENT_SUBNET" != */* ]]; then + CLIENT_SUBNET="$CLIENT_SUBNET/24" +fi + +# --- Generate Netplan YAML --- +echo "network:" > "$OUTPUT_FILE" +echo " version: 2" >> "$OUTPUT_FILE" +echo " renderer: $RENDERER" >> "$OUTPUT_FILE" +echo " ethernets:" >> "$OUTPUT_FILE" + +table_id=$START_TABLE + +for nic in "${NIC_LIST[@]}"; do + ip_cidr=$(get_ip_for_nic "$nic") + [[ -z "$ip_cidr" ]] && { errmsg "No IP address found for $nic"; exit 1; } + + ip_only="${ip_cidr%%/*}" + local_subnet=$(get_local_subnet "$nic") + + echo " $nic:" >> "$OUTPUT_FILE" + echo " dhcp4: no" >> "$OUTPUT_FILE" + [[ $NO_MTU == false ]] && echo " mtu: $MTU" >> "$OUTPUT_FILE" + echo " addresses:" >> "$OUTPUT_FILE" + echo " - $ip_cidr" >> "$OUTPUT_FILE" + + echo " routes:" >> "$OUTPUT_FILE" + # Local subnet: link-scoped, no via + echo " - to: $local_subnet" >> "$OUTPUT_FILE" + echo " table: $table_id" >> "$OUTPUT_FILE" + + # Optional client/default route + if [[ -n "$CLIENT_SUBNET" && -n "$GATEWAY" ]]; then + echo " - to: $CLIENT_SUBNET" >> "$OUTPUT_FILE" + echo " via: $GATEWAY" >> "$OUTPUT_FILE" + echo " table: $table_id" >> "$OUTPUT_FILE" + fi + + # Routing policy for SBR + echo " routing-policy:" >> "$OUTPUT_FILE" + echo " - from: $ip_only" >> "$OUTPUT_FILE" + echo " table: $table_id" >> "$OUTPUT_FILE" + + echo " ignore-carrier: true" >> "$OUTPUT_FILE" + + ((table_id++)) +done + +echo "βœ… Netplan file generated at $OUTPUT_FILE (review before applying)" + +# --- Generate sysctl file --- +: > "$SYSCTL_FILE" +echo "kernel.numa_balancing=0" >> "$SYSCTL_FILE" + +for nic in "${NIC_LIST[@]}"; do + echo "net.ipv4.conf.${nic}.arp_filter = 1" >> "$SYSCTL_FILE" + echo "net.ipv4.conf.${nic}.arp_ignore = 1" >> "$SYSCTL_FILE" + echo "net.ipv4.conf.${nic}.arp_announce = 2" >> "$SYSCTL_FILE" +done + +if $APPLY_SYSCTL; then + sysctl --system + echo "βœ… Sysctl applied immediately" +else + echo "βœ… Sysctl file generated: $SYSCTL_FILE (review. copy to /etc/sysctl.d and apply with 'sudo sysctl --system')" +fi + From 250fdcd7aa2fa3b77b2224b47888b38af4f4691d Mon Sep 17 00:00:00 2001 From: adamuk01 <85671778+adamuk01@users.noreply.github.com> Date: Wed, 12 Nov 2025 13:55:34 +0000 Subject: [PATCH 5/7] Updated netplan generator script to replace --client-subnet with default route when --gateway is specified --- preinstall/generate-netplan.sh | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/preinstall/generate-netplan.sh b/preinstall/generate-netplan.sh index 718688b..0500ae2 100755 --- a/preinstall/generate-netplan.sh +++ b/preinstall/generate-netplan.sh @@ -3,17 +3,19 @@ # generate-netplan.sh # # Generate a netplan YAML file for multiple NICs with source-based routing. -# Local subnets are link-scoped (no via), client/default routes use via/gateway. +# Local subnets are link-scoped (no via), default route uses gateway if provided. # Also generates sysctl file for ARP tuning and NUMA balancing. -# Does not apply netplan or sysctl automatically; admin must review before applying. -# The NICs might be defined in other files or separately +# Does not apply netplan automatically; admin can review before applying. +# Optional flags: +# --no-mtu : Do not set MTU to 9000 +# --renderer : Choose networkd (default) or NetworkManager +# --apply-sysctl : Apply sysctl immediately after generating set -euo pipefail OUTPUT_FILE="99-weka-netplan.yaml" SYSCTL_FILE="99-weka-sysctl.conf" NIC_LIST=() -CLIENT_SUBNET="" GATEWAY="" START_TABLE=101 MTU=9000 @@ -22,14 +24,13 @@ APPLY_SYSCTL=false RENDERER="networkd" usage() { - echo "Usage: $0 --nics [nic2 ...] [--client-subnet ] [--gateway ]" - echo " [--no-mtu] [--renderer networkd|NetworkManager] [--apply-sysctl]" + echo "Usage: $0 --nics [nic2 ...] [--gateway ] [--no-mtu] [--renderer networkd|NetworkManager] [--apply-sysctl]" exit 1 } errmsg() { echo "[ERROR] $*" >&2; } -# Parse CLI +# Parse CLI arguments while [[ $# -gt 0 ]]; do case "$1" in --nics) @@ -39,10 +40,6 @@ while [[ $# -gt 0 ]]; do shift done ;; - --client-subnet) - CLIENT_SUBNET="$2" - shift 2 - ;; --gateway) GATEWAY="$2" shift 2 @@ -68,6 +65,7 @@ done [[ ${#NIC_LIST[@]} -lt 1 ]] && { errmsg "At least one NIC must be specified"; usage; } +# Functions get_ip_for_nic() { ip -4 addr show "$1" | awk '/inet / {print $2}' | head -n1; } get_local_subnet() { @@ -81,11 +79,6 @@ get_local_subnet() { echo "$subnet" } -# Auto-add /mask if missing for client subnet -if [[ -n "$CLIENT_SUBNET" && "$CLIENT_SUBNET" != */* ]]; then - CLIENT_SUBNET="$CLIENT_SUBNET/24" -fi - # --- Generate Netplan YAML --- echo "network:" > "$OUTPUT_FILE" echo " version: 2" >> "$OUTPUT_FILE" @@ -112,9 +105,9 @@ for nic in "${NIC_LIST[@]}"; do echo " - to: $local_subnet" >> "$OUTPUT_FILE" echo " table: $table_id" >> "$OUTPUT_FILE" - # Optional client/default route - if [[ -n "$CLIENT_SUBNET" && -n "$GATEWAY" ]]; then - echo " - to: $CLIENT_SUBNET" >> "$OUTPUT_FILE" + # Default route if gateway specified + if [[ -n "$GATEWAY" ]]; then + echo " - to: 0.0.0.0/0" >> "$OUTPUT_FILE" echo " via: $GATEWAY" >> "$OUTPUT_FILE" echo " table: $table_id" >> "$OUTPUT_FILE" fi From e5a877b76a1fd4f7f275267551cf756ff7aabd63 Mon Sep 17 00:00:00 2001 From: adamuk01 <85671778+adamuk01@users.noreply.github.com> Date: Fri, 14 Nov 2025 09:46:44 +0000 Subject: [PATCH 6/7] Updated netplan generator script to add from: and add virtual IPs rules --- preinstall/generate-netplan.sh | 261 ++++++++++++++++++--------------- 1 file changed, 139 insertions(+), 122 deletions(-) diff --git a/preinstall/generate-netplan.sh b/preinstall/generate-netplan.sh index 0500ae2..6a1d4fb 100755 --- a/preinstall/generate-netplan.sh +++ b/preinstall/generate-netplan.sh @@ -2,142 +2,159 @@ # # generate-netplan.sh # -# Generate a netplan YAML file for multiple NICs with source-based routing. -# Local subnets are link-scoped (no via), default route uses gateway if provided. -# Also generates sysctl file for ARP tuning and NUMA balancing. -# Does not apply netplan automatically; admin can review before applying. -# Optional flags: -# --no-mtu : Do not set MTU to 9000 -# --renderer : Choose networkd (default) or NetworkManager -# --apply-sysctl : Apply sysctl immediately after generating +# Generates a Netplan YAML file for multiple NICs with static routing tables and SBR rules. +# +# Features: +# - Discovers IP/subnet for each NIC. +# - If --gateway is specified, adds a default route (0.0.0.0/0) in each NIC’s table. +# - Both default and local subnet routes include β€œfrom:” to set the source IP. +# - Optional --floating-ips adds virtual/floating IP routing-policy entries (shared by all NICs). +# - Optional --no-mtu disables MTU=9000 setting. +# - Also generates a sysctl file with ARP tuning and kernel.numa_balancing=0. +# * By default, only generates the file for review. +# * If --apply-sysctl is given, applies it immediately. +# - Netplan file is generated but NOT applied automatically. +# - Table IDs start from 101. +# +# Example: +# ./generate-netplan.sh --nics ens19 ens20 --gateway 10.0.255.254 +# ./generate-netplan.sh --nics ens19 ens20 --gateway 10.0.255.254 --floating-ips "10.0.1.100,10.0.1.101" --no-mtu --apply-sysctl +# set -euo pipefail -OUTPUT_FILE="99-weka-netplan.yaml" +# --- Defaults --- +NETPLAN_FILE="99-weka-netplan.yaml" SYSCTL_FILE="99-weka-sysctl.conf" -NIC_LIST=() -GATEWAY="" -START_TABLE=101 -MTU=9000 +START_TABLE_ID=101 NO_MTU=false APPLY_SYSCTL=false -RENDERER="networkd" - -usage() { - echo "Usage: $0 --nics [nic2 ...] [--gateway ] [--no-mtu] [--renderer networkd|NetworkManager] [--apply-sysctl]" - exit 1 -} - -errmsg() { echo "[ERROR] $*" >&2; } +FLOATING_IPS=() -# Parse CLI arguments +# --- Parse Args --- +NICS=() +GATEWAY="" while [[ $# -gt 0 ]]; do - case "$1" in - --nics) - shift - while [[ $# -gt 0 && "$1" != --* ]]; do - NIC_LIST+=("$1") - shift - done - ;; - --gateway) - GATEWAY="$2" - shift 2 - ;; - --no-mtu) - NO_MTU=true - shift - ;; - --renderer) - RENDERER="$2" - shift 2 - ;; - --apply-sysctl) - APPLY_SYSCTL=true - shift - ;; - *) - errmsg "Unknown argument: $1" - usage - ;; - esac + case "$1" in + --nics) + shift + while [[ $# -gt 0 && ! "$1" =~ ^-- ]]; do + NICS+=("$1") + shift + done + ;; + --gateway) + GATEWAY="$2" + shift 2 + ;; + --no-mtu) + NO_MTU=true + shift + ;; + --apply-sysctl) + APPLY_SYSCTL=true + shift + ;; + --floating-ips) + IFS=',' read -ra FLOATING_IPS <<< "$2" + shift 2 + ;; + *) + echo "Unknown argument: $1" + exit 1 + ;; + esac done -[[ ${#NIC_LIST[@]} -lt 1 ]] && { errmsg "At least one NIC must be specified"; usage; } - -# Functions -get_ip_for_nic() { ip -4 addr show "$1" | awk '/inet / {print $2}' | head -n1; } - -get_local_subnet() { - local nic="$1" - subnet=$(ip -o -f inet route show dev "$nic" scope link | awk '{print $1}' | head -n1) - if [[ -z "$subnet" ]]; then - ip_only=$(get_ip_for_nic "$nic") - subnet="${ip_only%/*}/24" - echo "⚠️ Could not determine network CIDR for $nic, defaulting to $subnet" - fi - echo "$subnet" -} - -# --- Generate Netplan YAML --- -echo "network:" > "$OUTPUT_FILE" -echo " version: 2" >> "$OUTPUT_FILE" -echo " renderer: $RENDERER" >> "$OUTPUT_FILE" -echo " ethernets:" >> "$OUTPUT_FILE" - -table_id=$START_TABLE - -for nic in "${NIC_LIST[@]}"; do - ip_cidr=$(get_ip_for_nic "$nic") - [[ -z "$ip_cidr" ]] && { errmsg "No IP address found for $nic"; exit 1; } - - ip_only="${ip_cidr%%/*}" - local_subnet=$(get_local_subnet "$nic") - - echo " $nic:" >> "$OUTPUT_FILE" - echo " dhcp4: no" >> "$OUTPUT_FILE" - [[ $NO_MTU == false ]] && echo " mtu: $MTU" >> "$OUTPUT_FILE" - echo " addresses:" >> "$OUTPUT_FILE" - echo " - $ip_cidr" >> "$OUTPUT_FILE" - - echo " routes:" >> "$OUTPUT_FILE" - # Local subnet: link-scoped, no via - echo " - to: $local_subnet" >> "$OUTPUT_FILE" - echo " table: $table_id" >> "$OUTPUT_FILE" - - # Default route if gateway specified - if [[ -n "$GATEWAY" ]]; then - echo " - to: 0.0.0.0/0" >> "$OUTPUT_FILE" - echo " via: $GATEWAY" >> "$OUTPUT_FILE" - echo " table: $table_id" >> "$OUTPUT_FILE" - fi - - # Routing policy for SBR - echo " routing-policy:" >> "$OUTPUT_FILE" - echo " - from: $ip_only" >> "$OUTPUT_FILE" - echo " table: $table_id" >> "$OUTPUT_FILE" - - echo " ignore-carrier: true" >> "$OUTPUT_FILE" - - ((table_id++)) +if [[ ${#NICS[@]} -eq 0 ]]; then + echo "No NICs specified. Use --nics ..." + exit 1 +fi + +# --- Start building Netplan YAML --- +echo "network:" > "$NETPLAN_FILE" +echo " version: 2" >> "$NETPLAN_FILE" +echo " renderer: networkd" >> "$NETPLAN_FILE" +echo " ethernets:" >> "$NETPLAN_FILE" + +table_id=$START_TABLE_ID + +for nic in "${NICS[@]}"; do + ip_with_mask=$(ip -4 -o addr show dev "$nic" | awk '{print $4}') + ip_only=$(echo "$ip_with_mask" | cut -d'/' -f1) + mask_bits=$(echo "$ip_with_mask" | cut -d'/' -f2) + + if [[ -z "$ip_with_mask" ]]; then + echo " Could not determine IP for $nic, skipping." + continue + fi + + # Derive subnet network address without ipcalc + network=$(ip -4 route show dev "$nic" | awk '/proto kernel/ {print $1; exit}') + if [[ -z "$network" ]]; then + echo " Could not determine network from $ip_with_mask, defaulting to /24" + network="$(echo "$ip_only" | cut -d'.' -f1-3).0/24" + fi + + echo " $nic:" >> "$NETPLAN_FILE" + echo " dhcp4: no" >> "$NETPLAN_FILE" + if [[ $NO_MTU == false ]]; then + echo " mtu: 9000" >> "$NETPLAN_FILE" + fi + echo " addresses:" >> "$NETPLAN_FILE" + echo " - $ip_with_mask" >> "$NETPLAN_FILE" + echo " routes:" >> "$NETPLAN_FILE" + echo " - to: $network" >> "$NETPLAN_FILE" + echo " from: $ip_only" >> "$NETPLAN_FILE" + echo " table: $table_id" >> "$NETPLAN_FILE" + + if [[ -n "$GATEWAY" ]]; then + # Add default route with β€œfrom” and β€œvia” + echo " - to: 0.0.0.0/0" >> "$NETPLAN_FILE" + echo " from: $ip_only" >> "$NETPLAN_FILE" + echo " via: $GATEWAY" >> "$NETPLAN_FILE" + echo " table: $table_id" >> "$NETPLAN_FILE" + fi + + echo " routing-policy:" >> "$NETPLAN_FILE" + echo " - from: $ip_only" >> "$NETPLAN_FILE" + echo " table: $table_id" >> "$NETPLAN_FILE" + echo " priority: $table_id" >> "$NETPLAN_FILE" + + # Add floating IPs if specified + if [[ ${#FLOATING_IPS[@]} -gt 0 ]]; then + echo " # Floating IPs" >> "$NETPLAN_FILE" + priority=201 + for fip in "${FLOATING_IPS[@]}"; do + echo " - from: $fip" >> "$NETPLAN_FILE" + echo " table: $table_id" >> "$NETPLAN_FILE" + echo " priority: $priority" >> "$NETPLAN_FILE" + ((priority++)) + done + fi + + echo " ignore-carrier: true" >> "$NETPLAN_FILE" + + ((table_id++)) done -echo "βœ… Netplan file generated at $OUTPUT_FILE (review before applying)" +echo "Netplan file generated: $NETPLAN_FILE (review before applying)" +echo # --- Generate sysctl file --- -: > "$SYSCTL_FILE" -echo "kernel.numa_balancing=0" >> "$SYSCTL_FILE" - -for nic in "${NIC_LIST[@]}"; do - echo "net.ipv4.conf.${nic}.arp_filter = 1" >> "$SYSCTL_FILE" - echo "net.ipv4.conf.${nic}.arp_ignore = 1" >> "$SYSCTL_FILE" - echo "net.ipv4.conf.${nic}.arp_announce = 2" >> "$SYSCTL_FILE" -done - -if $APPLY_SYSCTL; then - sysctl --system - echo "βœ… Sysctl applied immediately" +cat > "$SYSCTL_FILE" < Date: Tue, 25 Nov 2025 13:54:12 +0000 Subject: [PATCH 7/7] Added arp_filter=1 to generate-netplan.sh --- preinstall/generate-netplan.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/preinstall/generate-netplan.sh b/preinstall/generate-netplan.sh index 6a1d4fb..66662bd 100755 --- a/preinstall/generate-netplan.sh +++ b/preinstall/generate-netplan.sh @@ -147,8 +147,10 @@ cat > "$SYSCTL_FILE" <