#!/usr/bin/env bash # ============================================================ # DNS 全局配置脚本(含 IPv6 禁用 + 有线网卡静态 IP) # 功能: # 1. 全局固定 DNS(深圳电信) # 2. 全局禁用 IPv6(sysctl + GRUB + NM) # 3. 有线网卡支持用户输入静态 IP # 用法: sudo bash dns_setup.sh # ============================================================ set -euo pipefail RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' DNS_SERVERS="202.96.128.143 202.96.128.68 202.96.134.133 202.96.128.86 202.96.128.166" GRUB_FILE="/etc/default/grub" TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S') NOW_TS=$(date +%Y%m%d%H%M%S) echo "========================================" echo " DNS 全局配置 + IPv6 禁用脚本" echo "========================================" echo "" # 检查 root if [[ $EUID -ne 0 ]]; then echo -e "${RED}[错误] 请使用 sudo 运行此脚本${NC}" exit 1 fi # ────────────────────────────────────────── # 步骤 1: 配置 systemd-resolved 全局 DNS # ────────────────────────────────────────── echo "==> 步骤 1/6: 配置 systemd-resolved 全局 DNS" if [[ ! -f /etc/systemd/resolved.conf.bak ]]; then cp /etc/systemd/resolved.conf /etc/systemd/resolved.conf.bak echo -e "${GREEN}[备份] /etc/systemd/resolved.conf → resolved.conf.bak${NC}" fi cat > /etc/systemd/resolved.conf < 步骤 2/6: sysctl 全局禁用 IPv6" cat > /etc/sysctl.d/99-disable-ipv6.conf </dev/null echo -e "${GREEN}[完成] IPv6 已全局禁用 (sysctl)${NC}" if [[ "$(cat /proc/sys/net/ipv6/conf/all/disable_ipv6)" == "1" ]]; then echo -e "${GREEN}[验证] /proc/sys/net/ipv6/conf/all/disable_ipv6 = 1 ✓${NC}" fi # ────────────────────────────────────────── # 步骤 3: GRUB 内核参数禁用 IPv6 # ────────────────────────────────────────── echo "" echo "==> 步骤 3/6: GRUB 内核参数禁用 IPv6" if grep -q "ipv6.disable=1" "$GRUB_FILE" 2>/dev/null; then echo "[跳过] GRUB 已包含 ipv6.disable=1" else cp "$GRUB_FILE" "${GRUB_FILE}.bak.${NOW_TS}" sed -i 's/^GRUB_CMDLINE_LINUX_DEFAULT="\(.*\)"/GRUB_CMDLINE_LINUX_DEFAULT="\1 ipv6.disable=1"/' "$GRUB_FILE" sed -i 's/^GRUB_CMDLINE_LINUX="\(.*\)"/GRUB_CMDLINE_LINUX="\1 ipv6.disable=1"/' "$GRUB_FILE" update-grub &>/dev/null || grub2-mkconfig -o /boot/grub2/grub.cfg &>/dev/null echo -e "${GREEN}[完成] GRUB 内核参数已添加 ipv6.disable=1${NC}" echo -e "${YELLOW}[提示] GRUB 修改需重启后生效${NC}" fi # ────────────────────────────────────────── # 步骤 4: 有线网卡静态 IP 配置(含重复检测与清理) # ────────────────────────────────────────── echo "" echo "==> 步骤 4/6: 有线网卡检测与静态 IP 配置" echo "" # 收集所有有线网卡连接,按接口分组 declare -A IFACE_CONNS # 接口 -> "uuid|cname|uuid|cname|..." (用换行分隔多个) while IFS= read -r uuid; do ctype=$(nmcli -t -f connection.type connection show uuid "$uuid" 2>/dev/null | cut -d: -f2-) cname=$(nmcli -t -f connection.id connection show uuid "$uuid" 2>/dev/null | cut -d: -f2-) iface=$(nmcli -t -f connection.interface-name connection show uuid "$uuid" 2>/dev/null | cut -d: -f2-) if [[ "$ctype" == "802-3-ethernet" ]]; then if [[ -n "${IFACE_CONNS[$iface]:-}" ]]; then IFACE_CONNS[$iface]="${IFACE_CONNS[$iface]} $uuid|$cname" else IFACE_CONNS[$iface]="$uuid|$cname" fi fi done < <(nmcli -t -f UUID connection show 2>/dev/null) if [[ ${#IFACE_CONNS[@]} -eq 0 ]]; then echo " 未检测到有线网卡连接,跳过。" else NETPLAN_DIR="/etc/netplan" for iface in "${!IFACE_CONNS[@]}"; do # 解析该接口的所有连接 mapfile -t entries <<< "${IFACE_CONNS[$iface]}" if [[ ${#entries[@]} -gt 1 ]]; then echo "" echo -e " ${YELLOW}══════════════════════════════════════${NC}" echo -e " ${YELLOW} 检测到接口 [$iface] 有 ${#entries[@]} 个重复连接${NC}" echo -e " ${YELLOW}══════════════════════════════════════${NC}" # 找出活跃连接(有 IP 的) active_entry="" stale_uuids=() for e in "${entries[@]}"; do IFS='|' read -r euuid ecname <<< "$e" eip=$(ip -4 addr show "$iface" 2>/dev/null | grep -oP 'inet \K[\d.]+/[\d]+' | head -1) estate=$(nmcli -t -f GENERAL.STATE connection show uuid "$euuid" 2>/dev/null | cut -d: -f2-) echo "" echo " [$ecname] UUID: $euuid" echo " 当前 NM 状态: ${estate:-未知}" if [[ -n "$eip" ]]; then # 检查该连接的 IP 是否匹配接口 IP conn_ip=$(nmcli -t -f IP4.ADDRESS connection show uuid "$euuid" 2>/dev/null | cut -d: -f2- | head -1 | cut -d'/' -f1 | cut -d' ' -f1) iface_ip=$(echo "$eip" | cut -d'/' -f1) if [[ "$conn_ip" == "$iface_ip" ]] || [[ "$estate" == "activated" ]]; then active_entry="$e" echo -e " ${GREEN}◎ 活跃(当前使用中)${NC}" else stale_uuids+=("$euuid") echo -e " ${YELLOW}⊙ 残留/非活跃${NC}" fi else stale_uuids+=("$euuid") echo -e " ${YELLOW}⊙ 无 IP,疑似残留${NC}" fi done # 若未明确找到活跃连接,保留第一个,其余标记为残留 if [[ -z "$active_entry" ]]; then active_entry="${entries[0]}" stale_uuids=() for ((i=1; i<${#entries[@]}; i++)); do IFS='|' read -r suuid scname <<< "${entries[$i]}" stale_uuids+=("$suuid") done fi # 清理残留连接 if [[ ${#stale_uuids[@]} -gt 0 ]]; then echo "" echo -e " ${YELLOW}将自动清理 ${#stale_uuids[@]} 个残留连接:${NC}" for suuid in "${stale_uuids[@]}"; do sname=$(nmcli -t -f connection.id connection show uuid "$suuid" 2>/dev/null | cut -d: -f2-) echo " - $sname ($suuid)" done read -r -p " 确认清理?[Y/n] " do_clean if [[ ! "$do_clean" =~ ^[Nn]$ ]]; then for suuid in "${stale_uuids[@]}"; do sname=$(nmcli -t -f connection.id connection show uuid "$suuid" 2>/dev/null | cut -d: -f2-) nmcli connection delete "$suuid" 2>/dev/null || true rm -f "${NETPLAN_DIR}/90-NM-${suuid}.yaml" echo -e " ${GREEN}✓ 已删除: $sname${NC}" done else echo " 已跳过清理。" fi fi # 更新 entries 为仅保留活跃连接 entries=("$active_entry") fi # 对每个保留的有线连接,配置静态 IP for e in "${entries[@]}"; do IFS='|' read -r uuid cname <<< "$e" echo "" echo "────────────────────────────────────────" echo " 有线网卡: $cname" echo " 接口名: $iface" echo " UUID: $uuid" echo "" # 获取当前 IP 信息 cur_ip="" cur_gw="" if [[ -n "$iface" ]] && ip addr show "$iface" &>/dev/null; then cur_ip=$(ip -4 addr show "$iface" 2>/dev/null | grep -oP 'inet \K[\d.]+/[\d]+' | head -1) cur_gw=$(ip -4 route show default dev "$iface" 2>/dev/null | awk '{print $3}' | head -1) fi echo " 当前状态:" echo " IP 地址: ${cur_ip:-未获取}" echo " 网关: ${cur_gw:-未获取}" echo "" # 询问用户 read -r -p " 是否为此网卡设置静态 IP?[y/N] " set_static if [[ ! "$set_static" =~ ^[Yy]$ ]]; then echo -e " ${YELLOW}⊙ 跳过静态 IP,仅固定 DNS${NC}" cur_method=$(nmcli -t -f ipv4.method connection show uuid "$uuid" 2>/dev/null | cut -d: -f2-) if [[ "$cur_method" == "link-local" ]]; then echo -e " ${YELLOW}⊙ method=link-local,切换到 auto 以支持 DNS${NC}" nmcli connection modify "$uuid" ipv4.method auto fi nmcli connection modify "$uuid" ipv4.ignore-auto-dns yes nmcli connection modify "$uuid" ipv4.dns "${DNS_SERVERS// /,}" nmcli connection modify "$uuid" ipv6.method disabled continue fi # 输入静态 IP while true; do read -r -p " 请输入静态 IP 地址 (格式: x.x.x.x/yy): " static_ip if [[ "$static_ip" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/[0-9]+$ ]]; then break fi echo -e " ${RED}格式错误,示例: 192.168.1.90/24${NC}" done # 输入网关 read -r -p " 请输入网关地址 (回车使用检测值 ${cur_gw:-无}): " static_gw if [[ -z "$static_gw" ]]; then static_gw="$cur_gw" fi if [[ -z "$static_gw" ]]; then echo -e " ${RED}[错误] 网关不能为空${NC}" continue fi echo "" echo " 即将应用以下配置:" echo " 连接名: $cname" echo " 接口: $iface" echo " IP: $static_ip" echo " 网关: $static_gw" echo " DNS: $(echo "$DNS_SERVERS" | tr ' ' ',')" echo " IPv6: 禁用" echo "" read -r -p " 确认应用?[Y/n] " confirm if [[ "$confirm" =~ ^[Nn]$ ]]; then echo -e " ${YELLOW}⊙ 已取消${NC}" continue fi # ── 应用 NM 配置 ── nmcli connection modify "$uuid" \ ipv4.method manual \ ipv4.addresses "$static_ip" \ ipv4.gateway "$static_gw" \ ipv4.dns "${DNS_SERVERS// /,}" \ ipv6.method disabled echo -e " ${GREEN}✓ NM 配置已写入${NC}" # ── 同步 netplan ── NETPLAN_FILE="${NETPLAN_DIR}/90-NM-${uuid}.yaml" if [[ -d "$NETPLAN_DIR" ]]; then [[ ! -d "${NETPLAN_DIR}.bak" ]] && cp -r "$NETPLAN_DIR" "${NETPLAN_DIR}.bak" cat > "$NETPLAN_FILE" </dev/null; then echo -e " ${GREEN}✓ netplan 语法检查通过${NC}" else echo -e " ${YELLOW}⚠ netplan 语法检查有警告,请手动确认${NC}" fi fi echo "" done done fi # ────────────────────────────────────────── # 步骤 5: 配置 NetworkManager 其余连接 # ────────────────────────────────────────── echo "" echo "==> 步骤 5/6: 配置其余 NetworkManager 连接 (WiFi 等)" for conn in $(nmcli -t -f UUID connection show 2>/dev/null); do ctype=$(nmcli -t -f connection.type connection show uuid "$conn" 2>/dev/null | cut -d: -f2-) cname=$(nmcli -t -f connection.id connection show uuid "$conn" 2>/dev/null | cut -d: -f2-) # 跳过回环 if [[ "$ctype" == "loopback" ]]; then nmcli connection modify "$conn" ipv6.method auto 2>/dev/null || true echo " $cname → ${YELLOW}⊙ 回环,跳过${NC}" continue fi # 有线网卡已在步骤 4 处理 if [[ "$ctype" == "802-3-ethernet" ]]; then echo " $cname → ${YELLOW}⊙ 有线网卡,已在步骤 4 处理${NC}" continue fi # WiFi 等:固定 DNS + 禁用 IPv6 cur_method=$(nmcli -t -f ipv4.method connection show uuid "$conn" 2>/dev/null | cut -d: -f2-) if [[ "$cur_method" == "link-local" ]]; then nmcli connection modify "$conn" ipv4.method auto fi nmcli connection modify "$conn" ipv4.ignore-auto-dns yes nmcli connection modify "$conn" ipv4.dns "${DNS_SERVERS// /,}" nmcli connection modify "$conn" ipv6.method disabled echo " $cname (类型: $ctype) → ${GREEN}✓ DNS 固定 / IPv6 禁用${NC}" done # ────────────────────────────────────────── # 步骤 6: 重启服务 # ────────────────────────────────────────── echo "" echo "==> 步骤 6/6: 重启服务" systemctl restart systemd-resolved echo -e "${GREEN}[完成] systemd-resolved 已重启${NC}" # ────────────────────────────────────────── # 状态汇总 # ────────────────────────────────────────── echo "" echo "========================================" echo " 当前状态汇总" echo "========================================" echo "" echo "--- DNS 状态 ---" resolvectl status 2>&1 || true echo "" echo "--- IPv6 状态 ---" echo "sysctl disable_ipv6: $(cat /proc/sys/net/ipv6/conf/all/disable_ipv6 2>/dev/null || echo 'unknown')" ip -6 addr show 2>/dev/null | grep "inet6" || echo "(无 IPv6 地址)" echo "" echo "--- DNS 解析验证 ---" for domain in baidu.com qq.com; do result=$(resolvectl query "$domain" 2>&1 || true) echo " $domain → $(echo "$result" | head -3)" done echo "" echo -e "${GREEN}全部配置完成!${NC}" echo "" echo -e "${YELLOW}提示: GRUB 修改需重启后完全生效。${NC}" echo -e "${YELLOW} DNS / IPv6 变动需重连网络后生效。${NC}"