mirror of
https://github.com/oneclickvirt/pve.git
synced 2025-09-06 22:55:42 +08:00
856 lines
32 KiB
Bash
856 lines
32 KiB
Bash
#!/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 <<EOF | sudo tee -a /etc/network/interfaces
|
||
auto vmbr0
|
||
iface vmbr0 inet static
|
||
address $ipv4_address
|
||
gateway $ipv4_gateway
|
||
bridge_ports $interface
|
||
bridge_stp off
|
||
bridge_fd 0
|
||
EOF
|
||
}
|
||
|
||
# 添加带SLAAC的vmbr0
|
||
add_vmbr0_with_slaac() {
|
||
cat <<EOF | sudo tee -a /etc/network/interfaces
|
||
auto vmbr0
|
||
iface vmbr0 inet static
|
||
address $ipv4_address
|
||
gateway $ipv4_gateway
|
||
bridge_ports $interface
|
||
bridge_stp off
|
||
bridge_fd 0
|
||
|
||
iface vmbr0 inet6 auto
|
||
bridge_ports $interface
|
||
EOF
|
||
}
|
||
|
||
# 添加带双IPV6地址的vmbr0
|
||
add_vmbr0_with_dual_ipv6() {
|
||
last_ipv6=$(cat /usr/local/bin/pve_last_ipv6)
|
||
cat <<EOF | sudo tee -a /etc/network/interfaces
|
||
auto vmbr0
|
||
iface vmbr0 inet static
|
||
address $ipv4_address
|
||
gateway $ipv4_gateway
|
||
bridge_ports $interface
|
||
bridge_stp off
|
||
bridge_fd 0
|
||
|
||
iface vmbr0 inet6 static
|
||
address ${last_ipv6}
|
||
gateway ${ipv6_gateway}
|
||
|
||
iface vmbr0 inet6 static
|
||
address ${ipv6_address}/128
|
||
EOF
|
||
}
|
||
|
||
# 添加带单IPV6地址的vmbr0
|
||
add_vmbr0_with_single_ipv6() {
|
||
cat <<EOF | sudo tee -a /etc/network/interfaces
|
||
auto vmbr0
|
||
iface vmbr0 inet static
|
||
address $ipv4_address
|
||
gateway $ipv4_gateway
|
||
bridge_ports $interface
|
||
bridge_stp off
|
||
bridge_fd 0
|
||
|
||
iface vmbr0 inet6 static
|
||
address ${ipv6_address}/128
|
||
gateway ${ipv6_gateway}
|
||
EOF
|
||
}
|
||
|
||
# 配置vmbr1网桥
|
||
configure_vmbr1() {
|
||
if grep -q "vmbr1" /etc/network/interfaces; then
|
||
_blue "vmbr1 already exists in /etc/network/interfaces"
|
||
_blue "vmbr1 已存在在 /etc/network/interfaces"
|
||
elif [ -f "/usr/local/bin/iface_auto.txt" ]; then
|
||
add_vmbr1_with_accept_ra
|
||
elif [ -z "$ipv6_address" ] || [ -z "$ipv6_prefixlen" ] || [ -z "$ipv6_gateway" ] || [ "$status_he" = true ]; then
|
||
add_vmbr1_ipv4_only
|
||
else
|
||
add_vmbr1_with_ipv6
|
||
fi
|
||
}
|
||
|
||
# 添加带RA接受的vmbr1
|
||
add_vmbr1_with_accept_ra() {
|
||
cat <<EOF | sudo tee -a /etc/network/interfaces
|
||
auto vmbr1
|
||
iface vmbr1 inet static
|
||
address 172.16.1.1
|
||
netmask 255.255.255.0
|
||
bridge_ports none
|
||
bridge_stp off
|
||
bridge_fd 0
|
||
post-up echo 1 > /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 <<EOF | sudo tee -a /etc/network/interfaces
|
||
auto vmbr1
|
||
iface vmbr1 inet static
|
||
address 172.16.1.1
|
||
netmask 255.255.255.0
|
||
bridge_ports none
|
||
bridge_stp off
|
||
bridge_fd 0
|
||
post-up echo 1 > /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 <<EOF | sudo tee -a /etc/network/interfaces
|
||
auto vmbr1
|
||
iface vmbr1 inet static
|
||
address 172.16.1.1
|
||
netmask 255.255.255.0
|
||
bridge_ports none
|
||
bridge_stp off
|
||
bridge_fd 0
|
||
post-up echo 1 > /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 <<EOF
|
||
|
||
${temp_config}
|
||
EOF
|
||
cat <<EOF | sudo tee -a /etc/network/interfaces
|
||
|
||
auto vmbr2
|
||
iface vmbr2 inet6 static
|
||
address ${new_subnet}
|
||
bridge_ports none
|
||
bridge_stp off
|
||
bridge_fd 0
|
||
EOF
|
||
if [ -f "/usr/local/bin/ndpresponder" ]; then
|
||
new_exec_start="ExecStart=/usr/local/bin/ndpresponder -i he-ipv6 -n ${new_subnet}"
|
||
file_path="/etc/systemd/system/ndpresponder.service"
|
||
line_number=6
|
||
sed -i "${line_number}s|.*|${new_exec_start}|" "$file_path"
|
||
fi
|
||
configure_ipv6_forwarding
|
||
}
|
||
|
||
# 为IPV6子网配置vmbr2
|
||
configure_vmbr2_with_ipv6_subnet() {
|
||
appended_file="/usr/local/bin/pve_appended_content.txt"
|
||
if [ ! -s "$appended_file" ] && [ -f "/usr/local/bin/ndpresponder" ]; then
|
||
cat <<EOF | sudo tee -a /etc/network/interfaces
|
||
auto vmbr2
|
||
iface vmbr2 inet6 static
|
||
address ${ipv6_address}/${ipv6_prefixlen}
|
||
bridge_ports none
|
||
bridge_stp off
|
||
bridge_fd 0
|
||
EOF
|
||
new_exec_start="ExecStart=/usr/local/bin/ndpresponder -i vmbr0 -n ${ipv6_address_without_last_segment}0/${ipv6_prefixlen}"
|
||
file_path="/etc/systemd/system/ndpresponder.service"
|
||
line_number=6
|
||
sudo sed -i "${line_number}s|.*|${new_exec_start}|" "$file_path"
|
||
echo '*/2 * * * * curl -m 6 -s ipv6.ip.sb && curl -m 6 -s ipv6.ip.sb' | crontab -
|
||
fi
|
||
configure_ipv6_forwarding
|
||
}
|
||
|
||
|
||
# 配置IPV6转发设置
|
||
configure_ipv6_forwarding() {
|
||
update_sysctl "net.ipv6.conf.all.forwarding=1"
|
||
update_sysctl "net.ipv6.conf.all.proxy_ndp=1"
|
||
update_sysctl "net.ipv6.conf.default.proxy_ndp=1"
|
||
update_sysctl "net.ipv6.conf.vmbr0.proxy_ndp=1"
|
||
update_sysctl "net.ipv6.conf.vmbr1.proxy_ndp=1"
|
||
update_sysctl "net.ipv6.conf.vmbr2.proxy_ndp=1"
|
||
}
|
||
|
||
# 安装并配置IPtables
|
||
setup_iptables() {
|
||
apt-get install -y iptables iptables-persistent
|
||
iptables -t nat -A POSTROUTING -j MASQUERADE
|
||
update_sysctl "net.ipv4.ip_forward=1"
|
||
${sysctl_path} -p
|
||
}
|
||
|
||
restart_network_services() {
|
||
service networking restart
|
||
systemctl restart networking.service
|
||
sleep 3
|
||
ifreload -ad
|
||
iptables-save | awk '{if($1=="COMMIT"){delete x}}$1=="-A"?!x[$0]++:1' | iptables-restore
|
||
}
|
||
|
||
setup_ndpresponder() {
|
||
if [ -f "/usr/local/bin/ndpresponder" ] && [ -f "/etc/systemd/system/ndpresponder.service" ]; then
|
||
echo "Found ndpresponder binary and service file, setting up..."
|
||
systemctl daemon-reload
|
||
systemctl enable ndpresponder.service
|
||
systemctl start ndpresponder.service
|
||
systemctl status ndpresponder.service 2>/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 "你可以测试开一个虚拟机或者容器看看就知道是不是实际网络已应用成功了"
|