#!/usr/bin/env bash
set -euo pipefail
if [ "${EUID:-$(id -u)}" -ne 0 ]; then
echo "需要以 root 权限运行"
exit 1
fi
detect_manager() {
if command -v nmcli >/dev/null 2>&1 && systemctl is-active --quiet NetworkManager; then
echo "nm"; return
fi
if systemctl is-enabled --quiet systemd-networkd || [ -d /etc/systemd/network ]; then
echo "networkd"; return
fi
if [ -f /etc/network/interfaces ] || [ -d /etc/network/interfaces.d ]; then
echo "ifupdown"; return
fi
echo "unknown"
}
list_ifaces() {
ip -o link show | awk -F': ' '{print $2}' \
| awk '!/^lo$/ && !/^docker/ && !/^br/ && !/^veth/ && !/^vmnet/ && !/^tailscale/ && !/^zt/ && !/^wg/ && !/^tap/ && !/^tun/ {print}'
}
iface_defined_in_ifupdown() {
local iface="$1"
grep -Rqs "iface[[:space:]]\+$iface[[:space:]]\+inet" /etc/network/interfaces /etc/network/interfaces.d 2>/dev/null
}
nm_apply_dhcp() {
local iface="$1"
local conns
conns=$(nmcli -t -f NAME,DEVICE connection show | awk -F: -v d="$iface" '$2==d{print $1}')
if [ -z "$conns" ]; then
echo "NetworkManager:未找到绑定到 ${iface} 的连接"
return
fi
echo "NetworkManager:切换 ${iface} 到 DHCP"
while IFS= read -r c; do
nmcli connection modify "$c" ipv4.method auto ipv4.addresses "" ipv4.gateway "" ipv4.dns "" ipv4.never-default no connection.autoconnect yes
nmcli connection down "$c" || true
nmcli connection up "$c"
done <<< "$conns"
}
networkd_write_unit_minimal() {
local iface="$1"
local out="/etc/systemd/network/99-dhcp-${iface}.network"
install -m 0644 /dev/null "$out"
{
echo "[Match]"
echo "Name=${iface}"
echo "[Network]"
echo "DHCP=yes"
} > "$out"
if ! systemd-analyze verify "$out" >/dev/null 2>&1; then
rm -f "$out"
return 1
fi
echo "systemd-networkd:为 ${iface} 写入 DHCP 覆盖 ${out}"
return 0
}
networkd_reload_once() {
systemctl daemon-reload
if systemctl is-active --quiet systemd-networkd; then
systemctl reload systemd-networkd || systemctl restart systemd-networkd
else
systemctl reset-failed systemd-networkd || true
systemctl start systemd-networkd
fi
}
ifupdown_apply_dhcp_file() {
local file="$1"; shift
local targets_csv="$*"
[ -f "$file" ] || return 0
local bak="${file}.bak-$(date +%F-%H%M%S)"
cp -a "$file" "$bak"
echo "ifupdown:备份 ${file} -> ${bak} 并切换为 DHCP"
awk -v targets="$targets_csv" '
BEGIN{ split(targets, tarr, " "); for(i in tarr){ target[tarr[i]]=1 } skip=0 }
{
if ($1=="iface" && target[$2] && $4=="static") { print $1, $2, $3, "dhcp"; skip=1; next }
if (skip==1) {
if ($1=="iface") { skip=0 }
else if ($1 ~ /^(address|netmask|gateway|dns-nameservers)$/) { next }
}
print
}
' "$bak" > "${file}.new"
mv "${file}.new" "$file"
}
ifupdown_apply_dhcp() {
local iface="$1"
ifupdown_apply_dhcp_file "/etc/network/interfaces" "$iface"
for f in /etc/network/interfaces.d/*; do
[ -e "$f" ] && ifupdown_apply_dhcp_file "$f" "$iface"
done
ifdown "$iface" || true
ifup "$iface" || true
echo "ifupdown:${iface} 切换为 DHCP 完成"
}
main() {
local manager; manager=$(detect_manager)
if [ "$manager" = "unknown" ]; then
echo "无法检测网络管理方式"; exit 1
fi
echo "检测到网络管理方式:$manager"
local ifaces=("$@")
if [ "${#ifaces[@]}" -eq 0 ]; then
mapfile -t ifaces < <(list_ifaces)
fi
if [ "${#ifaces[@]}" -eq 0 ]; then
echo "未发现可操作网口"; exit 1
fi
local ok=(); local failed=()
case "$manager" in
nm)
for iface in "${ifaces[@]}"; do nm_apply_dhcp "$iface" || failed+=("$iface"); done
ok=("${ifaces[@]}")
;;
networkd)
for iface in "${ifaces[@]}"; do
if iface_defined_in_ifupdown "$iface"; then
ifupdown_apply_dhcp "$iface" && ok+=("$iface") || failed+=("$iface")
continue
fi
if networkd_write_unit_minimal "$iface"; then
ok+=("$iface")
else
echo "systemd-networkd:验证失败,尝试以 ifupdown 回退 ${iface}"
if ifupdown_apply_dhcp "$iface"; then
ok+=("$iface")
else
failed+=("$iface")
fi
fi
done
if [ "${#ok[@]}" -gt 0 ]; then
networkd_reload_once || true
for iface in "${ok[@]}"; do networkctl reconfigure "$iface" || networkctl up "$iface" || true; done
fi
;;
ifupdown)
for iface in "${ifaces[@]}"; do if ifupdown_apply_dhcp "$iface"; then ok+=("$iface"); else failed+=("$iface"); fi; done
;;
esac
if [ "${#ok[@]}" -gt 0 ]; then
echo "成功切换为 DHCP 的网口:${ok[*]}"
fi
if [ "${#failed[@]}" -gt 0 ]; then
echo "失败的网口:${failed[*]}"
exit 1
fi
}
main "$@"