pve/scripts/build_nat_network.sh

856 lines
32 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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 "你可以测试开一个虚拟机或者容器看看就知道是不是实际网络已应用成功了"