430 lines
16 KiB
Bash
430 lines
16 KiB
Bash
# shellcheck shell=sh
|
|
#
|
|
# Die Brücke über die primäre Netzkarte — Erkennen, Sichern, Zeitgeber, Bauen,
|
|
# Nachsehen.
|
|
#
|
|
# ---------------------------------------------------------------------------
|
|
# Warum das eine eigene Datei ist
|
|
# ---------------------------------------------------------------------------
|
|
#
|
|
# Diese Zeilen standen in `network.sh`, geschrieben für den stillgelegten
|
|
# Rettungssystem-Weg, und borgten sich vier Dinge von woanders:
|
|
# `detect_primary_interface` aus `proxmox.sh`, `log`, `http_get` und
|
|
# `CLUPILOT_PROBE_URL` aus `clupilot-bootstrap.sh`. Der Debian-Weg lädt sie
|
|
# einzeln auf den Host und fährt sie dort — geborgte Helfer wären dann nicht da.
|
|
#
|
|
# Also allein lauffähig. `network.sh` behält seine WireGuard- und
|
|
# nftables-Hälfte; diese Datei wird von beiden Wegen benutzt und existiert genau
|
|
# einmal. Eine zweite Fassung wären zwei Installationen, die bei jeder
|
|
# Proxmox-Version nachgezogen werden müssten — und die zweite fiele erst auf,
|
|
# wenn jemand sie benutzt.
|
|
#
|
|
# ---------------------------------------------------------------------------
|
|
# Warum aus dem LAUFENDEN Zustand abgeleitet wird
|
|
# ---------------------------------------------------------------------------
|
|
#
|
|
# Nicht aus `/etc/network/interfaces`. Was läuft, ist bei jedem Anbieter gleich
|
|
# strukturiert; wie es aufgeschrieben wurde, nicht. Das ist der Teil, der
|
|
# Hetzner und netcup zugleich trägt.
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Stellschrauben — überschreibbar, damit die Bibliothek ohne Host prüfbar ist
|
|
# ---------------------------------------------------------------------------
|
|
|
|
CLUPILOT_BRIDGE="${CLUPILOT_BRIDGE:-vmbr0}"
|
|
CLUPILOT_WORK_DIR="${CLUPILOT_WORK_DIR:-/var/lib/clupilot/bridge}"
|
|
CLUPILOT_INTERFACES_FILE="${CLUPILOT_INTERFACES_FILE:-/etc/network/interfaces}"
|
|
CLUPILOT_INTERFACES_D="${CLUPILOT_INTERFACES_D:-/etc/network/interfaces.d}"
|
|
CLUPILOT_SYS_NET="${CLUPILOT_SYS_NET:-/sys/class/net}"
|
|
CLUPILOT_IP="${CLUPILOT_IP:-ip}"
|
|
CLUPILOT_IFRELOAD="${CLUPILOT_IFRELOAD:-ifreload}"
|
|
CLUPILOT_PROBE_URL="${CLUPILOT_PROBE_URL:-http://deb.debian.org/}"
|
|
CLUPILOT_ROLLBACK_UNIT="${CLUPILOT_ROLLBACK_UNIT:-clupilot-network-rollback}"
|
|
CLUPILOT_NET_BACKUP="${CLUPILOT_NET_BACKUP:-/var/lib/clupilot/interfaces.vor-der-bruecke}"
|
|
CLUPILOT_WG_HUB_PUBKEY="${CLUPILOT_WG_HUB_PUBKEY:-}"
|
|
CLUPILOT_WG_HANDSHAKE_MAX_AGE="${CLUPILOT_WG_HANDSHAKE_MAX_AGE:-180}"
|
|
CLUPILOT_SYSTEMCTL="${CLUPILOT_SYSTEMCTL:-systemctl}"
|
|
CLUPILOT_UNIT_DIR="${CLUPILOT_UNIT_DIR:-/etc/systemd/system}"
|
|
CLUPILOT_SBIN_DIR="${CLUPILOT_SBIN_DIR:-/usr/local/sbin}"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Die zwei Helfer, die diese Datei früher geborgt hat
|
|
# ---------------------------------------------------------------------------
|
|
#
|
|
# Unter Definitionsschutz: `clupilot-bootstrap.sh` bringt eigene mit, und wer
|
|
# zuerst da ist, behält recht. Sonst überschriebe das Laden dieser Bibliothek
|
|
# die Protokollierung des Rettungssystem-Wegs.
|
|
|
|
if ! command -v log >/dev/null 2>&1; then
|
|
log() {
|
|
printf '%s %s\n' "$(date -u '+%Y-%m-%dT%H:%M:%SZ')" "$1"
|
|
}
|
|
fi
|
|
|
|
if ! command -v http_get >/dev/null 2>&1; then
|
|
http_get() {
|
|
if command -v curl >/dev/null 2>&1; then
|
|
curl -fsSL --retry 3 --retry-delay 2 --max-time 20 "$1"
|
|
else
|
|
wget -qO- --timeout=20 "$1"
|
|
fi
|
|
}
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Feststellen, was da ist
|
|
# ---------------------------------------------------------------------------
|
|
|
|
# Die Schnittstelle, über die die Vorgabe-Route geht. Nicht „die erste, die
|
|
# nicht lo heißt": eine Maschine mit zwei Karten hat oft eine angeschlossene und
|
|
# eine nicht.
|
|
detect_primary_interface() {
|
|
"$CLUPILOT_IP" -4 route show default 2>/dev/null \
|
|
| awk '{ for (i = 1; i < NF; i++) if ($i == "dev") { print $(i+1); exit } }'
|
|
}
|
|
|
|
# Ist das eine physische Karte?
|
|
#
|
|
# `bridge_ports` auf einem Bond oder einer bestehenden Bridge ist falsch — die
|
|
# Brücke nähme sich ihren eigenen Unterbau als Port, und was dabei herauskommt,
|
|
# ist von hier aus nicht mehr zu reparieren. VLAN-Geräte und Bonds haben keinen
|
|
# `device`-Verweis; die beiden weiteren Prüfungen sind der Gürtel dazu.
|
|
interface_is_physical() {
|
|
_if="${1:-}"
|
|
|
|
[ -n "$_if" ] || return 1
|
|
[ -e "${CLUPILOT_SYS_NET}/${_if}/device" ] || return 1
|
|
[ ! -d "${CLUPILOT_SYS_NET}/${_if}/bridge" ] || return 1
|
|
[ ! -d "${CLUPILOT_SYS_NET}/${_if}/bonding" ] || return 1
|
|
|
|
return 0
|
|
}
|
|
|
|
# Kam die Adresse per DHCP?
|
|
#
|
|
# Aus dem laufenden Zustand: der Kernel markiert eine geleaste Adresse mit
|
|
# `dynamic`. Das steht bei jedem Anbieter gleich da — anders als die Datei, in
|
|
# die er es geschrieben hat.
|
|
address_is_dynamic() {
|
|
"$CLUPILOT_IP" -4 -o addr show dev "$1" scope global 2>/dev/null \
|
|
| grep -q '[[:space:]]dynamic[[:space:]]'
|
|
}
|
|
|
|
# Wie der Anbieter das Netz aufzieht. Eine Brücke, die für den einen Fall
|
|
# richtig ist, nimmt den anderen vom Netz.
|
|
#
|
|
# - `dhcp` — die Adresse kommt per DHCP (Cloud-Produkte).
|
|
# - `routed` — geroutete Einzeladresse, Gateway AUSSERHALB des eigenen Subnetzes
|
|
# (Hetzner-dediziert mit /32). Braucht eine pointopoint-Route,
|
|
# sonst findet der Kernel das Gateway nicht.
|
|
# - `subnet` — gewöhnliches Subnetz, Gateway darin.
|
|
detect_network_style() {
|
|
_iface="${1:-}"
|
|
[ -n "$_iface" ] || _iface="$(detect_primary_interface)"
|
|
|
|
# Laufender Zustand zuerst. Die Datei des Anbieters ist nur das Zweitsignal
|
|
# — sie sagt, was jemand aufgeschrieben hat, nicht was gilt.
|
|
if address_is_dynamic "$_iface"; then
|
|
printf 'dhcp'
|
|
return 0
|
|
fi
|
|
|
|
if grep -qsE "iface[[:space:]]+${_iface}[[:space:]]+inet[[:space:]]+dhcp" \
|
|
"$CLUPILOT_INTERFACES_FILE" "${CLUPILOT_INTERFACES_D}"/* 2>/dev/null; then
|
|
printf 'dhcp'
|
|
return 0
|
|
fi
|
|
|
|
_cidr="$("$CLUPILOT_IP" -4 -o addr show dev "$_iface" scope global 2>/dev/null | awk '{ print $4; exit }')"
|
|
_gw="$("$CLUPILOT_IP" -4 route show default 2>/dev/null | awk '{ print $3; exit }')"
|
|
_prefix="${_cidr##*/}"
|
|
|
|
# /32 heißt: das eigene Subnetz besteht aus der eigenen Adresse. Ein Gateway
|
|
# darin kann es nicht geben.
|
|
if [ "$_prefix" = '32' ]; then
|
|
printf 'routed'
|
|
return 0
|
|
fi
|
|
|
|
# Liegt das Gateway im eigenen Subnetz? `ip route get` beantwortet das, ohne
|
|
# dass dieses Skript Netzmasken rechnen muss — und rechnet dabei mit
|
|
# derselben Logik, die der Kernel später anwendet.
|
|
if [ -n "$_gw" ] && "$CLUPILOT_IP" -4 route get "$_gw" 2>/dev/null | grep -q "dev ${_iface}.*src"; then
|
|
printf 'subnet'
|
|
return 0
|
|
fi
|
|
|
|
printf 'routed'
|
|
}
|
|
|
|
bridge_exists() {
|
|
"$CLUPILOT_IP" link show "${1:-$CLUPILOT_BRIDGE}" >/dev/null 2>&1
|
|
}
|
|
|
|
# Trägt die Brücke wirklich den Verkehr, oder existiert sie nur?
|
|
#
|
|
# Eine `vmbr0` ohne Adresse und ohne Vorgaberoute ist eine Brücke im Sinne von
|
|
# `ip link show` und sonst nichts. Der alte Schritt prüfte genau das und warf das
|
|
# Ergebnis weg; sein Kommentar behauptete, er halte die Abwesenheit fest, und er
|
|
# hielt nichts fest.
|
|
bridge_carries_default_route() {
|
|
_want="${1:-$CLUPILOT_BRIDGE}"
|
|
_dev="$("$CLUPILOT_IP" -4 route show default 2>/dev/null \
|
|
| awk '{ for (i = 1; i < NF; i++) if ($i == "dev") { print $(i+1); exit } }')"
|
|
|
|
[ "$_dev" = "$_want" ]
|
|
}
|
|
|
|
bridge_has_address() {
|
|
[ -n "$("$CLUPILOT_IP" -4 -o addr show dev "${1:-$CLUPILOT_BRIDGE}" scope global 2>/dev/null | awk '{ print $4; exit }')" ]
|
|
}
|
|
|
|
# Die drei zusammen. „Da" reicht nicht — sie muss tragen.
|
|
bridge_is_up() {
|
|
bridge_exists "$CLUPILOT_BRIDGE" \
|
|
&& bridge_carries_default_route "$CLUPILOT_BRIDGE" \
|
|
&& bridge_has_address "$CLUPILOT_BRIDGE"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Die Strophe
|
|
# ---------------------------------------------------------------------------
|
|
|
|
# Zusatzrouten des Anbieters — alles auf der Karte, was der Kernel nicht von
|
|
# selbst wieder anlegt.
|
|
#
|
|
# Ausgelassen werden zwei Sorten, und beide mit Grund: die Kernel-Route zum
|
|
# eigenen Subnetz (`proto kernel`) entsteht, sobald die Adresse steht, und die
|
|
# Link-Route zum Gateway legt `pointopoint` an. Sie trotzdem einzutragen ließe
|
|
# `ip route add` scheitern — und ein gescheitertes `up` nimmt bei `ifreload`
|
|
# die ganze Strophe mit.
|
|
extra_routes() {
|
|
_iface="$1"
|
|
_gw="$2"
|
|
|
|
"$CLUPILOT_IP" -4 route show dev "$_iface" 2>/dev/null | while IFS= read -r _route; do
|
|
[ -n "$_route" ] || continue
|
|
|
|
case "$_route" in
|
|
default*) continue ;;
|
|
"${_gw} "*|"${_gw}") continue ;;
|
|
*'proto kernel'*) continue ;;
|
|
esac
|
|
|
|
printf ' up ip route add %s dev %s || true\n' "$_route" "$CLUPILOT_BRIDGE"
|
|
done
|
|
}
|
|
|
|
# Eine statische, globale IPv6-Adresse — oder nichts.
|
|
#
|
|
# `dynamic` (SLAAC/DHCPv6) und `temporary` (Privacy Extensions) fliegen raus:
|
|
# was der Kernel selbst vergibt, gehört nicht in eine Datei geschrieben.
|
|
static_ipv6_on() {
|
|
"$CLUPILOT_IP" -6 -o addr show dev "$1" scope global 2>/dev/null \
|
|
| grep -v '[[:space:]]dynamic[[:space:]]' \
|
|
| grep -v '[[:space:]]temporary[[:space:]]' \
|
|
| awk '{ print $4; exit }'
|
|
}
|
|
|
|
ipv6_gateway() {
|
|
"$CLUPILOT_IP" -6 route show default 2>/dev/null \
|
|
| awk '{ for (i = 1; i < NF; i++) if ($i == "via") { print $(i+1); exit } }'
|
|
}
|
|
|
|
# Schreibt die Strophe — und sonst nichts. Kein `ifreload`, kein Sichern, kein
|
|
# Zeitgeber. Getrennt von build_bridge, damit genau das prüfbar ist, was eine
|
|
# Maschine umbringt, ohne dafür ein Netz neu laden zu müssen.
|
|
write_bridge_stanza() {
|
|
_iface="$1"
|
|
_style="$2"
|
|
_cidr="$3"
|
|
_gw="$4"
|
|
|
|
_ip4="${_cidr%%/*}"
|
|
_mac="$(cat "${CLUPILOT_SYS_NET}/${_iface}/address" 2>/dev/null)"
|
|
|
|
case "$_style" in
|
|
dhcp)
|
|
_inet="iface ${CLUPILOT_BRIDGE} inet dhcp"
|
|
_addr=''
|
|
;;
|
|
routed)
|
|
# Gateway außerhalb des eigenen Subnetzes. Ohne `pointopoint` findet
|
|
# der Kernel keinen Weg dorthin: die Route zeigte auf ein Netz, in
|
|
# dem das Gateway nicht liegt, und die Maschine wäre still weg.
|
|
_inet="iface ${CLUPILOT_BRIDGE} inet static"
|
|
_addr=" address ${_ip4}/32
|
|
pointopoint ${_gw}
|
|
gateway ${_gw}"
|
|
;;
|
|
*)
|
|
_inet="iface ${CLUPILOT_BRIDGE} inet static"
|
|
_addr=" address ${_cidr}
|
|
gateway ${_gw}"
|
|
;;
|
|
esac
|
|
|
|
# Festgenagelt: eine Brücke wählt sonst die kleinste MAC ihrer Ports, und
|
|
# manche Anbieter binden den Switchport an die der Karte. Bei einem Port ist
|
|
# das dieselbe — aber „ist dieselbe" und „bleibt dieselbe" sind zweierlei.
|
|
_hw=''
|
|
[ -n "$_mac" ] && _hw=" hwaddress ether ${_mac}"
|
|
|
|
_inet6=''
|
|
_v6="$(static_ipv6_on "$_iface")"
|
|
_v6gw="$(ipv6_gateway)"
|
|
|
|
if [ -n "$_v6" ] && [ -n "$_v6gw" ]; then
|
|
_inet6="
|
|
iface ${CLUPILOT_BRIDGE} inet6 static
|
|
address ${_v6}
|
|
gateway ${_v6gw}"
|
|
elif [ -n "$_v6" ] || [ -n "$_v6gw" ]; then
|
|
log 'IPv6 wird NICHT mit auf die Brücke genommen: keine statische globale Adresse mit Standardgateway (SLAAC/DHCPv6 werden bewusst nicht nachgebaut — Proxmox setzt forwarding=1, und der Kernel verwirft RAs dann ohne accept_ra=2).'
|
|
fi
|
|
|
|
cat > "$CLUPILOT_INTERFACES_FILE" <<EOF
|
|
# Von CluPilot geschrieben (Schritt EnsureNetworkBridge), weil auf dieser
|
|
# Maschine keine ${CLUPILOT_BRIDGE} lag: Proxmox auf Debian legt keine an, nur
|
|
# der ISO-Installer tut das.
|
|
#
|
|
# Abgeleitet aus dem LAUFENDEN Zustand, nicht aus der Datei des Anbieters.
|
|
# Anbieterform: ${_style}. Karte: ${_iface}.
|
|
#
|
|
# Die vorherige Fassung liegt unter ${CLUPILOT_NET_BACKUP}.tar.gz.
|
|
source ${CLUPILOT_INTERFACES_D}/*
|
|
|
|
auto lo
|
|
iface lo inet loopback
|
|
|
|
iface ${_iface} inet manual
|
|
|
|
auto ${CLUPILOT_BRIDGE}
|
|
${_inet}
|
|
${_addr}
|
|
${_hw}
|
|
bridge-ports ${_iface}
|
|
bridge-stp off
|
|
bridge-fd 0
|
|
$(extra_routes "$_iface" "$_gw")${_inet6}
|
|
EOF
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Die Selbstrücknahme
|
|
# ---------------------------------------------------------------------------
|
|
#
|
|
# Sichern, Zeitgeber auf fünf Minuten, umstellen, nachsehen, abbestellen.
|
|
#
|
|
# Der Zeitgeber ist eine systemd-Einheit und kein `sleep &`: ein Hintergrundlauf
|
|
# stirbt mit seiner Sitzung, und die Sitzung ist genau das, was abreißt, wenn die
|
|
# Umstellung schiefgeht. Er ist der einzige Grund, warum dieser Abschnitt die
|
|
# Netzkonfiguration überhaupt anfassen darf.
|
|
#
|
|
# Abbestellt wird er NICHT vom Treiber. Das tut CluPilot, nachdem es sich über
|
|
# den Tunnel neu verbunden und nachgesehen hat — ein Skript auf dem Host kann
|
|
# über seine eigene Erreichbarkeit von außen nur raten.
|
|
|
|
backup_network_config() {
|
|
mkdir -p "$(dirname -- "$CLUPILOT_NET_BACKUP")"
|
|
tar czf "${CLUPILOT_NET_BACKUP}.tar.gz" \
|
|
-C / "${CLUPILOT_INTERFACES_FILE#/}" \
|
|
$( [ -d "$CLUPILOT_INTERFACES_D" ] && printf '%s' "${CLUPILOT_INTERFACES_D#/}" ) \
|
|
2>/dev/null
|
|
}
|
|
|
|
# Der Inhalt des Rücknahme-Skripts — als Text, nicht als Datei.
|
|
#
|
|
# Getrennt, damit die Reihenfolge darin prüfbar ist, ohne systemd zu brauchen.
|
|
# Und die Reihenfolge ist der Punkt: das Urteil wird geschrieben, BEVOR
|
|
# zurückgespielt wird, damit auch ein halb geglücktes Zurückspielen einen Grund
|
|
# hinterlässt. `rolled-back` kommt zuletzt — das ist das Signal, auf das CluPilot
|
|
# wartet, und es darf erst stehen, wenn der alte Zustand wirklich zurück ist.
|
|
render_rollback_script() {
|
|
_minutes="${1:-5}"
|
|
|
|
cat <<EOF
|
|
#!/bin/sh
|
|
# Von CluPilot gestellt (Schritt EnsureNetworkBridge). Spielt die
|
|
# Netzkonfiguration von VOR der Bruecke zurueck, weil sie binnen ${_minutes}
|
|
# Minuten nicht abbestellt wurde — und abbestellen kann CluPilot nur, wenn es
|
|
# den Host ueber den Tunnel wieder erreicht.
|
|
printf 'failed' > '${CLUPILOT_WORK_DIR}/state'
|
|
printf 'Die Bruecke nahm den Host vom Netz; die Netzkonfiguration von vorher wurde zurueckgespielt.' > '${CLUPILOT_WORK_DIR}/note'
|
|
|
|
tar xzf '${CLUPILOT_NET_BACKUP}.tar.gz' -C /
|
|
if command -v ifreload >/dev/null 2>&1; then
|
|
ifreload -a
|
|
else
|
|
systemctl restart networking
|
|
fi
|
|
|
|
logger -t clupilot 'Netzkonfiguration zurueckgespielt: die Bruecke hat den Host vom Netz genommen.'
|
|
|
|
# Erst jetzt. Vorher hiesse es: zurueckgespielt, obwohl es noch laeuft.
|
|
: > '${CLUPILOT_WORK_DIR}/rolled-back'
|
|
|
|
# Selbst wegraeumen, sonst liegen Unit-Dateien herum, die aussehen, als stuende
|
|
# noch eine Ruecknahme aus.
|
|
rm -f '${CLUPILOT_UNIT_DIR}/${CLUPILOT_ROLLBACK_UNIT}.timer' \\
|
|
'${CLUPILOT_UNIT_DIR}/${CLUPILOT_ROLLBACK_UNIT}.service'
|
|
systemctl daemon-reload 2>/dev/null || true
|
|
EOF
|
|
}
|
|
|
|
# Stellt den Zeitgeber. Ab hier gibt es einen Weg zurück, und erst ab hier darf
|
|
# irgendetwas am Netz verändert werden.
|
|
schedule_network_rollback() {
|
|
_minutes="${1:-5}"
|
|
|
|
mkdir -p "$CLUPILOT_SBIN_DIR" "$CLUPILOT_UNIT_DIR" "$CLUPILOT_WORK_DIR"
|
|
|
|
render_rollback_script "$_minutes" > "${CLUPILOT_SBIN_DIR}/${CLUPILOT_ROLLBACK_UNIT}.sh"
|
|
chmod 700 "${CLUPILOT_SBIN_DIR}/${CLUPILOT_ROLLBACK_UNIT}.sh"
|
|
|
|
cat > "${CLUPILOT_UNIT_DIR}/${CLUPILOT_ROLLBACK_UNIT}.service" <<EOF
|
|
[Unit]
|
|
Description=CluPilot: Netzkonfiguration zurückspielen, wenn die Brücke den Host vom Netz nimmt
|
|
|
|
[Service]
|
|
Type=oneshot
|
|
ExecStart=${CLUPILOT_SBIN_DIR}/${CLUPILOT_ROLLBACK_UNIT}.sh
|
|
EOF
|
|
|
|
cat > "${CLUPILOT_UNIT_DIR}/${CLUPILOT_ROLLBACK_UNIT}.timer" <<EOF
|
|
[Unit]
|
|
Description=CluPilot: Frist für die Selbstrücknahme der Netzumstellung
|
|
|
|
[Timer]
|
|
OnActiveSec=${_minutes}min
|
|
AccuracySec=1s
|
|
EOF
|
|
|
|
"$CLUPILOT_SYSTEMCTL" daemon-reload
|
|
"$CLUPILOT_SYSTEMCTL" start "${CLUPILOT_ROLLBACK_UNIT}.timer"
|
|
log "Selbstrücknahme steht: ohne Abbestellung läuft sie in ${_minutes} Minuten"
|
|
}
|
|
|
|
cancel_network_rollback() {
|
|
"$CLUPILOT_SYSTEMCTL" stop "${CLUPILOT_ROLLBACK_UNIT}.timer" 2>/dev/null || true
|
|
rm -f "${CLUPILOT_UNIT_DIR}/${CLUPILOT_ROLLBACK_UNIT}.timer" \
|
|
"${CLUPILOT_UNIT_DIR}/${CLUPILOT_ROLLBACK_UNIT}.service" \
|
|
"${CLUPILOT_SBIN_DIR}/${CLUPILOT_ROLLBACK_UNIT}.sh"
|
|
"$CLUPILOT_SYSTEMCTL" daemon-reload 2>/dev/null || true
|
|
log 'Selbstrücknahme abbestellt'
|
|
}
|
|
|
|
# Strophe schreiben und anwenden. Ab hier ist die Leitung in Gefahr.
|
|
build_bridge() {
|
|
write_bridge_stanza "$@"
|
|
|
|
log 'Strophe geschrieben:'
|
|
cat "$CLUPILOT_INTERFACES_FILE"
|
|
|
|
if command -v "$CLUPILOT_IFRELOAD" >/dev/null 2>&1; then
|
|
"$CLUPILOT_IFRELOAD" -a
|
|
else
|
|
systemctl restart networking
|
|
fi
|
|
}
|