#!/bin/bash # from # https://github.com/oneclickvirt/pve # 2025.08.14 ########## 预设部分输出和部分中间变量 _red() { echo -e "\033[31m\033[01m$@\033[0m"; } _green() { echo -e "\033[32m\033[01m$@\033[0m"; } _yellow() { echo -e "\033[33m\033[01m$@\033[0m"; } _blue() { echo -e "\033[36m\033[01m$@\033[0m"; } reading() { read -rp "$(_green "$1")" "$2"; } export DEBIAN_FRONTEND=noninteractive utf8_locale=$(locale -a 2>/dev/null | grep -i -m 1 -E "UTF-8|utf8") if [[ -z "$utf8_locale" ]]; then echo "No UTF-8 locale found" else export LC_ALL="$utf8_locale" export LANG="$utf8_locale" export LANGUAGE="$utf8_locale" echo "Locale set to $utf8_locale" fi rm -rf /usr/local/bin/build_backend_pve.txt check_cdn() { local o_url=$1 local shuffled_cdn_urls=($(shuf -e "${cdn_urls[@]}")) # 打乱数组顺序 for cdn_url in "${shuffled_cdn_urls[@]}"; do if curl -sL -k "$cdn_url$o_url" --max-time 6 | grep -q "success" >/dev/null 2>&1; then export cdn_success_url="$cdn_url" return fi sleep 0.5 done export cdn_success_url="" } check_cdn_file() { check_cdn "https://raw.githubusercontent.com/spiritLHLS/ecs/main/back/test" if [ -n "$cdn_success_url" ]; then _yellow "CDN available, using CDN" else _yellow "No CDN available, no use CDN" fi } get_system_arch() { local sysarch="$(uname -m)" if [ "${sysarch}" = "unknown" ] || [ "${sysarch}" = "" ]; then local sysarch="$(arch)" fi # 根据架构信息设置系统位数并下载文件,其余 * 包括了 x86_64 case "${sysarch}" in "i386" | "i686" | "x86_64") system_arch="x86" ;; "armv7l" | "armv8" | "armv8l" | "aarch64") system_arch="arm" ;; *) system_arch="" ;; esac } check_interface() { if [ -z "$interface_2" ]; then interface=${interface_1} return elif [ -n "$interface_1" ] && [ -n "$interface_2" ]; then if ! grep -q "$interface_1" "/etc/network/interfaces" && ! grep -q "$interface_2" "/etc/network/interfaces" && [ -f "/etc/network/interfaces.d/50-cloud-init" ]; then if grep -q "$interface_1" "/etc/network/interfaces.d/50-cloud-init" || grep -q "$interface_2" "/etc/network/interfaces.d/50-cloud-init"; then if ! grep -q "$interface_1" "/etc/network/interfaces.d/50-cloud-init" && grep -q "$interface_2" "/etc/network/interfaces.d/50-cloud-init"; then interface=${interface_2} return elif ! grep -q "$interface_2" "/etc/network/interfaces.d/50-cloud-init" && grep -q "$interface_1" "/etc/network/interfaces.d/50-cloud-init"; then interface=${interface_1} return fi fi fi if grep -q "$interface_1" "/etc/network/interfaces"; then interface=${interface_1} return elif grep -q "$interface_2" "/etc/network/interfaces"; then interface=${interface_2} return else interfaces_list=$(ip addr show | awk '/^[0-9]+: [^lo]/ {print $2}' | cut -d ':' -f 1) interface="" for iface in $interfaces_list; do if [[ "$iface" = "$interface_1" || "$iface" = "$interface_2" ]]; then interface="$iface" fi done if [ -z "$interface" ]; then interface="eth0" fi return fi else interface="eth0" return fi _red "Physical interface not found, exit execution" _red "找不到物理接口,退出执行" exit 1 } update_sysctl() { sysctl_config="$1" # 格式: key=value key="${sysctl_config%%=*}" value="${sysctl_config#*=}" # 目标配置文件(systemd 方式) custom_conf="/etc/sysctl.d/99-custom.conf" mkdir -p /etc/sysctl.d # 检查 /etc/sysctl.conf 是否存在并且在系统加载路径中 use_etc_sysctl_conf=false if [ -f /etc/sysctl.conf ]; then if grep -q "/etc/sysctl.conf" /etc/sysctl.d/README* 2>/dev/null || \ grep -q "/etc/sysctl.conf" /lib/systemd/system/sysctl.service 2>/dev/null; then use_etc_sysctl_conf=true fi fi # 更新 /etc/sysctl.d/99-custom.conf if grep -q "^$sysctl_config" "$custom_conf" 2>/dev/null; then : # 已经有正确配置,跳过 elif grep -q "^#$sysctl_config" "$custom_conf" 2>/dev/null; then sed -i "s/^#$sysctl_config/$sysctl_config/" "$custom_conf" elif grep -q "^$key" "$custom_conf" 2>/dev/null; then sed -i "s|^$key.*|$sysctl_config|" "$custom_conf" else echo "$sysctl_config" >> "$custom_conf" fi # 如果系统还在用 /etc/sysctl.conf,也同步更新 if [ "$use_etc_sysctl_conf" = true ]; then if grep -q "^$sysctl_config" /etc/sysctl.conf; then : # 已经有正确配置 elif grep -q "^#$sysctl_config" /etc/sysctl.conf; then sed -i "s/^#$sysctl_config/$sysctl_config/" /etc/sysctl.conf elif grep -q "^$key" /etc/sysctl.conf; then sed -i "s|^$key.*|$sysctl_config|" /etc/sysctl.conf else echo "$sysctl_config" >> /etc/sysctl.conf fi fi sysctl -w "$key=$value" >/dev/null 2>&1 } remove_duplicate_lines() { chattr -i "$1" # 预处理:去除行尾空格和制表符 sed -i 's/[ \t]*$//' "$1" # 去除重复行并跳过空行和注释行 if [ -f "$1" ]; then awk '{ line = $0; gsub(/^[ \t]+/, "", line); gsub(/[ \t]+/, " ", line); if (!NF || !seen[line]++) print $0 }' "$1" >"$1.tmp" && mv -f "$1.tmp" "$1" fi chattr +i "$1" } is_private_ipv6() { local address=$1 local temp="0" # 输入为空 if [[ ! -n $address ]]; then temp="1" fi # 输入不含:符号 if [[ -n $address && $address != *":"* ]]; then temp="2" fi # 检查IPv6地址是否以fe80开头(链接本地地址) if [[ $address == fe80:* ]]; then temp="3" fi # 检查IPv6地址是否以fc00或fd00开头(唯一本地地址) if [[ $address == fc00:* || $address == fd00:* ]]; then temp="4" fi # 检查IPv6地址是否以2001:db8开头(文档前缀) if [[ $address == 2001:db8* ]]; then temp="5" fi # 检查IPv6地址是否以::1开头(环回地址) if [[ $address == ::1 ]]; then temp="6" fi # 检查IPv6地址是否以::ffff:开头(IPv4映射地址) if [[ $address == ::ffff:* ]]; then temp="7" fi # 检查IPv6地址是否以2002:开头(6to4隧道地址) if [[ $address == 2002:* ]]; then temp="8" fi # 检查IPv6地址是否以2001:开头(Teredo隧道地址) if [[ $address == 2001:* ]]; then temp="9" fi if [ "$temp" -gt 0 ]; then # 非公网情况 return 0 else # 其他情况为公网地址 return 1 fi } check_ipv6() { IPV6=$(ip -6 addr show | grep global | awk '{print length, $2}' | sort -nr | head -n 1 | awk '{print $2}' | cut -d '/' -f1) if [ ! -f /usr/local/bin/pve_last_ipv6 ] || [ ! -s /usr/local/bin/pve_last_ipv6 ] || [ "$(sed -e '/^[[:space:]]*$/d' /usr/local/bin/pve_last_ipv6)" = "" ]; then ipv6_list=$(ip -6 addr show | grep global | awk '{print length, $2}' | sort -nr | awk '{print $2}') line_count=$(echo "$ipv6_list" | wc -l) if [ "$line_count" -ge 2 ]; then # 获取最后一行的内容 last_ipv6=$(echo "$ipv6_list" | tail -n 1) # 切分最后一个:之前的内容 last_ipv6_prefix="${last_ipv6%:*}:" # 与${ipv6_gateway}比较是否相同 if [ "${last_ipv6_prefix}" = "${ipv6_gateway%:*}:" ]; then echo $last_ipv6 >/usr/local/bin/pve_last_ipv6 fi _green "The local machine is bound to more than one IPV6 address" _green "本机绑定了不止一个IPV6地址" fi fi if is_private_ipv6 "$IPV6"; then # 由于是内网IPV6地址,需要通过API获取外网地址 IPV6="" API_NET=("ipv6.ip.sb" "https://ipget.net" "ipv6.ping0.cc" "https://api.my-ip.io/ip" "https://ipv6.icanhazip.com") for p in "${API_NET[@]}"; do response=$(curl -sLk6m8 "$p" | tr -d '[:space:]') if [ $? -eq 0 ] && ! (echo "$response" | grep -q "error"); then IPV6="$response" break fi sleep 1 done fi echo $IPV6 >/usr/local/bin/pve_check_ipv6 } ########## 查询信息 # 安装必要工具 install_required_tools() { local tools=("lshw" "ipcalc" "sipcalc" "ovs-vsctl:openvswitch-switch" "crontab:cron") for tool in "${tools[@]}"; do local cmd="${tool%%:*}" local pkg="${tool#*:}" if [[ "$pkg" == "$cmd" ]]; then pkg="$cmd"; fi if ! command -v "$cmd" >/dev/null 2>&1; then apt-get install -y "$pkg" fi done apt-get install -y net-tools } # 请求IPV6网络以加载配置 request_ipv6() { curl -m 5 ipv6.ip.sb || curl -m 5 ipv6.ip.sb } # 检测物理接口和MAC地址 detect_network_interfaces() { interface_1=$(lshw -C network | awk '/logical name:/{print $3}' | sed -n '1p') interface_2=$(lshw -C network | awk '/logical name:/{print $3}' | sed -n '2p') check_interface if [ ! -f /usr/local/bin/pve_mac_address ] || [ ! -s /usr/local/bin/pve_mac_address ] || [ "$(sed -e '/^[[:space:]]*$/d' /usr/local/bin/pve_mac_address)" = "" ]; then mac_address=$(ip -o link show dev ${interface} | awk '{print $17}') echo "$mac_address" >/usr/local/bin/pve_mac_address fi mac_address=$(cat /usr/local/bin/pve_mac_address) setup_persistent_net_link } # 设置持久化网络接口名称 setup_persistent_net_link() { if [ ! -f /etc/systemd/network/10-persistent-net.link ]; then echo '[Match]' >/etc/systemd/network/10-persistent-net.link echo "MACAddress=${mac_address}" >>/etc/systemd/network/10-persistent-net.link echo "" >>/etc/systemd/network/10-persistent-net.link echo '[Link]' >>/etc/systemd/network/10-persistent-net.link echo "Name=${interface}" >>/etc/systemd/network/10-persistent-net.link /etc/init.d/udev force-reload fi } # 检测HE隧道配置 detect_he_tunnel() { status_he=false if grep -q "he-ipv6" /etc/network/interfaces; then wget ${cdn_success_url}https://raw.githubusercontent.com/oneclickvirt/6in4/main/covert.sh -O /root/covert.sh chmod 777 covert.sh ./covert.sh sleep 1 status_he=true chattr -i /etc/network/interfaces temp_config=$(awk '/auto he-ipv6/{flag=1; print $0; next} flag && flag++<10' /etc/network/interfaces) sed -i '/^auto he-ipv6/,/^$/d' /etc/network/interfaces chattr +i /etc/network/interfaces ipv6_address=$(echo "$temp_config" | awk '/address/ {print $2}') ipv6_gateway=$(echo "$temp_config" | awk '/gateway/ {print $2}') ipv6_prefixlen=$(ifconfig he-ipv6 | grep -oP 'prefixlen \K\d+' | head -n 1) target_mask=${ipv6_prefixlen} ((target_mask += 8 - ($target_mask % 8))) echo "$target_mask" >/usr/local/bin/pve_ipv6_prefixlen ipv6_subnet_2=$(sipcalc --v6split=${target_mask} ${ipv6_gateway}/${ipv6_prefixlen} | awk '/Network/{n++} n==2' | awk '{print $3}' | grep -v '^$') ipv6_subnet_2_without_last_segment="${ipv6_subnet_2%:*}:" new_subnet="${ipv6_subnet_2_without_last_segment}1/${target_mask}" echo ${ipv6_subnet_2_without_last_segment}1 >/usr/local/bin/pve_check_ipv6 echo $ipv6_gateway >/usr/local/bin/pve_ipv6_gateway else detect_existing_ipv6_config fi check_fe80_gateway } # 检测已有的IPV6配置 detect_existing_ipv6_config() { if [ -f /usr/local/bin/pve_ipv6_prefixlen ]; then ipv6_prefixlen=$(cat /usr/local/bin/pve_ipv6_prefixlen) fi if [ -f /usr/local/bin/pve_ipv6_gateway ]; then ipv6_gateway=$(cat /usr/local/bin/pve_ipv6_gateway) fi if [ -f /usr/local/bin/pve_check_ipv6 ]; then ipv6_address=$(cat /usr/local/bin/pve_check_ipv6) ipv6_address_without_last_segment="${ipv6_address%:*}:" reconfigure_ipv6_address fi } # 重新配置IPV6地址 reconfigure_ipv6_address() { if [[ $ipv6_address != *:: && $ipv6_address_without_last_segment != *:: ]]; then ipv6_address=$(sipcalc -i ${ipv6_address}/${ipv6_prefixlen} | grep "Subnet prefix (masked)" | cut -d ' ' -f 4 | cut -d '/' -f 1 | sed 's/:0:0:0:0:/::/' | sed 's/:0:0:0:/::/') ipv6_address="${ipv6_address%:*}:1" if [ "$ipv6_address" == "$ipv6_gateway" ]; then ipv6_address="${ipv6_address%:*}:2" fi ipv6_address_without_last_segment="${ipv6_address%:*}:" if ping -c 1 -6 -W 3 $ipv6_address >/dev/null 2>&1; then check_ipv6 ipv6_address=$(cat /usr/local/bin/pve_check_ipv6) echo "${ipv6_address}" >/usr/local/bin/pve_check_ipv6 ipv6_address_without_last_segment="${ipv6_address%:*}:" fi elif [[ $ipv6_address == *:: ]]; then ipv6_address="${ipv6_address}1" if [ "$ipv6_address" == "$ipv6_gateway" ]; then ipv6_address="${ipv6_address%:*}:2" fi ipv6_address_without_last_segment="${ipv6_address%:*}:" echo "${ipv6_address}" >/usr/local/bin/pve_check_ipv6 fi } # 检查fe80类型网关 check_fe80_gateway() { if [[ $ipv6_gateway == fe80* ]]; then ipv6_gateway_fe80="Y" else ipv6_gateway_fe80="N" fi fe80_address=$(cat /usr/local/bin/pve_fe80_address) } # 配置文件重试下载,避免网络原因导致配置失败 download_with_retry() { local url="$1" local output="$2" local max_attempts=5 local attempt=1 local delay=1 while [ $attempt -le $max_attempts ]; do wget -q "$url" -O "$output" && return 0 echo "Download failed: $url, try $attempt, wait $delay seconds and retry..." echo "下载失败:$url,尝试第 $attempt 次,等待 $delay 秒后重试..." sleep $delay attempt=$((attempt + 1)) delay=$((delay * 2)) [ $delay -gt 30 ] && delay=30 done red "Download failed: $url, maximum number of attempts exceeded ($max_attempts)" red "下载失败:$url,超过最大尝试次数 ($max_attempts)" return 1 } # 配置ndpresponder守护进程 install_ndpresponder() { appended_file="/usr/local/bin/pve_appended_content.txt" if [ -n "$ipv6_address" ] && [ -n "$ipv6_prefixlen" ] && [ -n "$ipv6_gateway" ] && [ ! -s "$appended_file" ]; then if [ -f /usr/local/bin/pve_maximum_subset ] && [ "$(cat /usr/local/bin/pve_maximum_subset)" = false ]; then _blue "No install ndpresponder" elif [ "$system_arch" = "x86" ] || [ "$system_arch" = "x86_64" ]; then download_with_retry "${cdn_success_url}https://github.com/oneclickvirt/pve/releases/download/ndpresponder_x86/ndpresponder" "/usr/local/bin/ndpresponder" || return 1 download_with_retry "${cdn_success_url}https://raw.githubusercontent.com/oneclickvirt/pve/main/extra_scripts/ndpresponder.service" "/etc/systemd/system/ndpresponder.service" || return 1 chmod 755 /usr/local/bin/ndpresponder chmod 644 /etc/systemd/system/ndpresponder.service elif [ "$system_arch" = "arm" ]; then download_with_retry "${cdn_success_url}https://github.com/oneclickvirt/pve/releases/download/ndpresponder_aarch64/ndpresponder" "/usr/local/bin/ndpresponder" || return 1 download_with_retry "${cdn_success_url}https://raw.githubusercontent.com/oneclickvirt/pve/main/extra_scripts/ndpresponder.service" "/etc/systemd/system/ndpresponder.service" || return 1 chmod 755 /usr/local/bin/ndpresponder chmod 644 /etc/systemd/system/ndpresponder.service fi fi } # 检测IPV4相关信息 detect_ipv4_info() { if [ -f /usr/local/bin/pve_ipv4_address ]; then ipv4_address=$(cat /usr/local/bin/pve_ipv4_address) else ipv4_address=$(ip addr show | awk '/inet .*global/ && !/inet6/ {print $2}' | sed -n '1p') echo "$ipv4_address" >/usr/local/bin/pve_ipv4_address fi if [ -f /usr/local/bin/pve_ipv4_gateway ]; then ipv4_gateway=$(cat /usr/local/bin/pve_ipv4_gateway) else ipv4_gateway=$(ip route | awk '/default/ {print $3}' | sed -n '1p') echo "$ipv4_gateway" >/usr/local/bin/pve_ipv4_gateway fi if [ -f /usr/local/bin/pve_ipv4_subnet ]; then ipv4_subnet=$(cat /usr/local/bin/pve_ipv4_subnet) else ipv4_subnet=$(ipcalc -n "$ipv4_address" | grep -oP 'Netmask:\s+\K.*' | awk '{print $1}') echo "$ipv4_subnet" >/usr/local/bin/pve_ipv4_subnet fi } # 备份和修复网络配置文件 prepare_network_interfaces() { if [ ! -f /etc/network/interfaces.bak ]; then cp /etc/network/interfaces /etc/network/interfaces.bak fi # 修正部分网络设置重复的错误 if [[ -f "/etc/network/interfaces.d/50-cloud-init" && -f "/etc/network/interfaces" ]]; then if grep -q "auto lo" "/etc/network/interfaces.d/50-cloud-init" && grep -q "iface lo inet loopback" "/etc/network/interfaces.d/50-cloud-init" && grep -q "auto lo" "/etc/network/interfaces" && grep -q "iface lo inet loopback" "/etc/network/interfaces"; then chattr -i /etc/network/interfaces.d/50-cloud-init sed -i '/auto lo/d' "/etc/network/interfaces.d/50-cloud-init" sed -i '/iface lo inet loopback/d' "/etc/network/interfaces.d/50-cloud-init" chattr +i /etc/network/interfaces.d/50-cloud-init fi fi if [ -f "/etc/network/interfaces.new" ]; then chattr -i /etc/network/interfaces.new rm -rf /etc/network/interfaces.new fi chattr -i /etc/network/interfaces check_loopback_config } # 检查回环接口配置 check_loopback_config() { if ! grep -q "auto lo" /etc/network/interfaces; then _blue "Can not find 'auto lo' in /etc/network/interfaces" exit 1 fi if ! grep -q "iface lo inet loopback" /etc/network/interfaces; then _blue "Can not find 'iface lo inet loopback' in /etc/network/interfaces" exit 1 fi } # 配置vmbr0网桥 configure_vmbr0() { chattr -i /etc/network/interfaces if grep -q "vmbr0" "/etc/network/interfaces"; then _blue "vmbr0 already exists in /etc/network/interfaces" _blue "vmbr0 已存在在 /etc/network/interfaces" else # 根据不同情况添加vmbr0配置 if [ -z "$ipv6_address" ] || [ -z "$ipv6_prefixlen" ] || [ -z "$ipv6_gateway" ] && [ ! -f /usr/local/bin/pve_last_ipv6 ]; then # 无IPV6地址情况 add_vmbr0_ipv4_only elif [ -f /usr/local/bin/pve_slaac_status ] && [ $(cat /usr/local/bin/pve_maximum_subset) = false ] && [ ! -f /usr/local/bin/pve_last_ipv6 ]; then # 有IPV6地址,只有一个IPV6地址且后续仅使用一个IPV6地址,存在slaac机制 add_vmbr0_with_slaac elif [ -f /usr/local/bin/pve_last_ipv6 ]; then # 有IPV6地址,不只一个IPV6地址,一个用作网关,一个用作实际地址 add_vmbr0_with_dual_ipv6 else # 有IPV6地址,只有一个IPV6地址,但后续使用最大IPV6子网范围 add_vmbr0_with_single_ipv6 fi fi # 如果不是fe80类型网关,添加fe80地址删除命令 if [[ "${ipv6_gateway_fe80}" == "N" ]]; then chattr -i /etc/network/interfaces echo " up ip addr del $fe80_address dev $interface" >>/etc/network/interfaces remove_duplicate_lines "/etc/network/interfaces" chattr -i /etc/network/interfaces fi # 如果IPV6地址是写死附加上的,这块附加回vmbr0,方便后续使用ip6tables进行转发 appended_file="/usr/local/bin/pve_appended_content.txt" if [ -s "$appended_file" ]; then chattr -i /etc/network/interfaces sed -E 's/(# control-alias) [^[:space:]]+/\1 vmbr0/g; s/(iface) [^[:space:]]+/\1 vmbr0/g' "$appended_file" | sudo tee -a /etc/network/interfaces > /dev/null # 如果需要配DNAT/SNAT的V6转发,那么fe80加白就没必要了,需要注释掉 sed -i '/^[[:space:]]*up ip addr del fe80/s/^/#/' /etc/network/interfaces grep -Fxq 'post-up echo 1 > /proc/sys/net/ipv6/conf/vmbr0/proxy_ndp' /etc/network/interfaces || echo 'post-up echo 1 > /proc/sys/net/ipv6/conf/vmbr0/proxy_ndp' >> /etc/network/interfaces fi } # 仅添加IPV4配置的vmbr0 add_vmbr0_ipv4_only() { cat < /proc/sys/net/ipv4/ip_forward post-up echo 1 > /proc/sys/net/ipv4/conf/vmbr1/proxy_arp post-up iptables -t nat -A POSTROUTING -s '172.16.1.0/24' -o vmbr0 -j MASQUERADE post-down iptables -t nat -D POSTROUTING -s '172.16.1.0/24' -o vmbr0 -j MASQUERADE pre-up echo 2 > /proc/sys/net/ipv6/conf/vmbr0/accept_ra EOF } # 仅添加IPV4配置的vmbr1 add_vmbr1_ipv4_only() { cat < /proc/sys/net/ipv4/ip_forward post-up echo 1 > /proc/sys/net/ipv4/conf/vmbr1/proxy_arp post-up iptables -t nat -A POSTROUTING -s '172.16.1.0/24' -o vmbr0 -j MASQUERADE post-down iptables -t nat -D POSTROUTING -s '172.16.1.0/24' -o vmbr0 -j MASQUERADE EOF } # 添加带IPV6配置的vmbr1 add_vmbr1_with_ipv6() { cat < /proc/sys/net/ipv4/ip_forward post-up echo 1 > /proc/sys/net/ipv4/conf/vmbr1/proxy_arp post-up iptables -t nat -A POSTROUTING -s '172.16.1.0/24' -o vmbr0 -j MASQUERADE post-down iptables -t nat -D POSTROUTING -s '172.16.1.0/24' -o vmbr0 -j MASQUERADE iface vmbr1 inet6 static address 2001:db8:1::1/64 post-up sysctl -w net.ipv6.conf.all.forwarding=1 post-up ip6tables -t nat -A POSTROUTING -s 2001:db8:1::/64 -o vmbr0 -j MASQUERADE post-down sysctl -w net.ipv6.conf.all.forwarding=0 post-down ip6tables -t nat -D POSTROUTING -s 2001:db8:1::/64 -o vmbr0 -j MASQUERADE EOF } # 配置vmbr2网桥(如果需要) configure_vmbr2() { appended_file="/usr/local/bin/pve_appended_content.txt" if [ -n "$ipv6_prefixlen" ] && [ "$((ipv6_prefixlen))" -le 112 ] && [ ! -s "$appended_file" ]; then if grep -q "vmbr2" /etc/network/interfaces; then _blue "vmbr2 already exists in /etc/network/interfaces" _blue "vmbr2 已存在在 /etc/network/interfaces" elif [ -f /usr/local/bin/pve_maximum_subset ] && [ $(cat /usr/local/bin/pve_maximum_subset) = false ]; then _blue "No set vmbr2" elif [ "$status_he" = true ]; then configure_vmbr2_with_he_tunnel elif [ ! -z "$ipv6_address" ] && [ ! -z "$ipv6_prefixlen" ] && [ ! -z "$ipv6_gateway" ] && [ ! -z "$ipv6_address_without_last_segment" ]; then configure_vmbr2_with_ipv6_subnet else rm -rf /etc/systemd/system/ndpresponder.service fi elif [ -s "$appended_file" ]; then tmp_script="/usr/local/bin/check_ipv6.sh" echo '#!/bin/bash' > "$tmp_script" echo "" >> "$tmp_script" counter=0 grep -Po '(?<=address )[\da-fA-F:]+(?=/64)' "$appended_file" | while read -r ip; do delay=$((counter * 6)) echo "sleep $delay; curl --interface $ip -6 -s https://ifconfig.co &" >> "$tmp_script" counter=$((counter + 1)) done echo "wait" >> "$tmp_script" chmod +x "$tmp_script" (crontab -l 2>/dev/null; echo "*/15 * * * * bash $tmp_script") | sort -u | crontab - else rm -rf /etc/systemd/system/ndpresponder.service fi } # 为HE隧道配置vmbr2 configure_vmbr2_with_he_tunnel() { chattr -i /etc/network/interfaces sudo tee -a /etc/network/interfaces </dev/null return 0 else echo "ndpresponder binary or service file not found." return 1 fi } backup_and_clean_interfaces() { if [ ! -f /etc/network/interfaces_nat.bak ]; then cp /etc/network/interfaces /etc/network/interfaces_nat.bak chattr -i /etc/network/interfaces input_file="/etc/network/interfaces" output_file="/etc/network/interfaces.tmp" start_pattern="iface lo inet loopback" end_pattern="auto vmbr0" delete_lines=0 while IFS= read -r line; do if [[ $line == *"$start_pattern"* ]]; then delete_lines=1 fi if [ $delete_lines -eq 0 ] || [[ $line == *"$start_pattern"* ]] || [[ $line == *"$end_pattern"* ]]; then echo "$line" >>"$output_file" fi if [[ $line == *"$end_pattern"* ]]; then delete_lines=0 fi done <"$input_file" mv "$output_file" "$input_file" chattr +i /etc/network/interfaces fi } clean_cache_files() { if [ -f "/etc/network/interfaces.new" ]; then chattr -i /etc/network/interfaces.new rm -rf /etc/network/interfaces.new fi } check_ndpresponder_status() { appended_file="/usr/local/bin/pve_appended_content.txt" if [ ! -s "$appended_file" ]; then service_status=$(systemctl is-active ndpresponder.service) if [[ "$service_status" == "active" || "$service_status" == "activating" ]]; then _green "The ndpresponder service started successfully and is running, and the host can open a service with a separate IPV6 address." _green "ndpresponder服务启动成功且正在运行,宿主机可开设带独立IPV6地址的服务。" else if grep -q "vmbr2" /etc/network/interfaces; then _green "Please perform reboot to reboot the server to load the IPV6 configuration, otherwise IPV6 is not available" _green "请执行 reboot 重启服务器以加载IPV6配置,否则IPV6不可用" else _green "The status of the ndpresponder service is abnormal and the host can not open a service with a separate IPV6 address." _green "ndpresponder服务状态异常,宿主机不可开设带独立IPV6地址的服务。" fi fi elif [ -s "$appended_file" ]; then _green "Additional IPv6 addresses exist for mapping by NAT, and the host can open services with separate IPV6 addresses." _green "存在额外的IPv6地址可供NAT进行映射,宿主机可开设带独立IPV6地址的服务。" fi } install_required_tools request_ipv6 cdn_urls=("https://cdn0.spiritlhl.top/" "http://cdn3.spiritlhl.net/" "http://cdn1.spiritlhl.net/" "https://ghproxy.com/" "http://cdn2.spiritlhl.net/") check_cdn_file get_system_arch sysctl_path=$(which sysctl) detect_network_interfaces detect_he_tunnel install_ndpresponder detect_ipv4_info prepare_network_interfaces configure_vmbr0 configure_vmbr1 configure_vmbr2 chattr +i /etc/network/interfaces rm -rf /usr/local/bin/iface_auto.txt setup_iptables restart_network_services setup_ndpresponder backup_and_clean_interfaces clean_cache_files systemctl start check-dns.service sleep 3 check_ndpresponder_status sleep 1 _green "It is recommended to restart the server once to apply the new configuration." _green "强烈推荐重启一次服务器,以应用新配置,避免配置不生效的问题。" _green "you can test open a virtual machine or container to see if the actual network has been applied successfully" _green "你可以测试开一个虚拟机或者容器看看就知道是不是实际网络已应用成功了"