0. 前言

之前我们实现了在 vps 上的 IPv6 池代理, 现在弄一个更适合家宽的

1. systcl 配置

# /etc/sysctl.d/sysctl.conf

net.ipv6.conf.default.forwarding=1
net.ipv6.conf.all.forwarding=1
net.ipv6.conf.all.proxy_ndp=1
net.ipv6.ip_nonlocal_bind=1
net.core.default_qdisc = cake
net.ipv4.tcp_congestion_control = bbr
net.ipv4.tcp_fastopen = 3

2. ndppd 配置

# /etc/ndppd

route-ttl 30000
proxy ens18 {
    router yes
    timeout 60
    ttl 300
    rule 240e:1234:5678:abcd::/64 {
        static
    }
}

3. Xray 配置

这里需要修改 outbounds 里的 sendThrough

{
  "log": {
    "loglevel": "warning"
  },
  "inbounds": [
    {
      "port": 10808,
      "protocol": "socks",
      "settings": {
        "auth": "noauth",
        "udp": true,
        "ip": "127.0.0.1"
      }
    }
  ],
  "outbounds": [
    {
      "tag": "ipv6-proxy-pool",
      "protocol": "freedom",
      "sendThrough": "240e:1234:5678:abcd::/64"
    },
    {
      "tag": "blocked",
      "protocol": "blackhole"
    }
  ],
  "routing": {
    "domainStrategy": "AsIs",
    "rules": [
      {
        "type": "field",
        "outboundTag": "ipv6-proxy-pool",
        "port": "1-65535"
      }
    ]
  }
}

4. Systemd 配置, 实现动态切换

实现清除宽带重播后的旧 v6, 放在 /usr/local/bin/cleanup-deprecated-ipv6.sh

#!/bin/bash
while true; do
  ip -6 addr | grep deprecated | awk '{print $2}' | cut -d'/' -f1 | while read addr; do
    [ -n "$addr" ] && ip -6 addr del $addr/64 dev ens18
  done
  sleep 1
done
# /etc/systemd/system/cleanup-deprecated-ipv6.service
[Unit]
Description=Auto cleanup deprecated IPv6 addresses
After=network.target

[Service]
ExecStart=/usr/local/bin/cleanup-deprecated-ipv6.sh
Restart=always

[Install]
WantedBy=multi-user.target

实现 v6 变动后, 路由, xray 以及 ndppd 的配置变更, 记得修改网卡, 脚本放在 /usr/local/bin/update-ipv6-prefix.sh

#!/bin/bash
set -e

DEV="ens18"
CONF="/etc/ndppd.conf"
XRAY_CONF="/usr/local/etc/xray/config.json"
LOG_FILE="/var/log/ipv6-prefix-update.log"

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}

# 更新 Xray 配置中的 sendThrough
update_xray_config() {
    local new_prefix="$1"
    
    if [ ! -f "$XRAY_CONF" ]; then
        log "⚠️ Xray配置文件不存在: $XRAY_CONF"
        return 1
    fi
    
    # 备份原配置
    cp "$XRAY_CONF" "${XRAY_CONF}.bak"
    
    # 使用 jq 更新 sendThrough 字段(如果系统有 jq)
    if command -v jq &>/dev/null; then
        jq --arg prefix "$new_prefix" \
           '.outbounds |= map(if .tag == "ipv6-proxy-pool" then .sendThrough = $prefix else . end)' \
           "$XRAY_CONF" > "${XRAY_CONF}.tmp" && mv "${XRAY_CONF}.tmp" "$XRAY_CONF"
        log "✅ 使用 jq 更新 Xray 配置"
    else
        # 使用 sed 作为备选方案
        sed -i -E "s|(\"sendThrough\":\s*\")[^\"]+(\")|\1${new_prefix}\2|g" "$XRAY_CONF"
        log "✅ 使用 sed 更新 Xray 配置"
    fi
    
    # 重启 Xray 服务
    if systemctl is-active --quiet xray; then
        log "重启 Xray 服务..."
        systemctl restart xray || log "⚠️ 无法重启 Xray,请检查服务状态。"
    else
        log "⚠️ Xray 服务未运行,跳过重启。"
    fi
}

# 确保日志文件存在
mkdir -p "$(dirname "$LOG_FILE")"
touch "$LOG_FILE"

# 等待网络设备就绪
while ! ip link show dev "$DEV" &>/dev/null; do
    log "等待网络接口 $DEV ..."
    sleep 2
done

# 初始化时强制检查一次
FIRST_RUN=true

while true; do
    # 获取接口的全局 IPv6 地址,排除 ULA (fd00::/8) 和链路本地地址 (fe80::/10)
    FULL_ADDR=$(ip -6 addr show dev $DEV scope global 2>/dev/null | \
                grep -oP '([0-9a-f:]+)/64' | \
                grep -v '^fd[0-9a-f][0-9a-f]:' | \
                grep -v '^fe[89ab][0-9a-f]:' | \
                head -n1 | \
                cut -d'/' -f1)
    
    if [ -z "$FULL_ADDR" ]; then
        sleep 2
        continue
    fi
    
    NEW_PREFIX=$(echo "$FULL_ADDR" | awk -F: '{print $1":"$2":"$3":"$4"::/64"}')
    OLD_PREFIX=$(grep -oP 'rule\s+\K[0-9a-f:]+::/64' "$CONF" 2>/dev/null | head -n1)
    
    # 第一次运行或前缀变化时触发
    if [ "$FIRST_RUN" = true ] || [ "$OLD_PREFIX" != "$NEW_PREFIX" ]; then
        log "检测到IPv6前缀更新:${OLD_PREFIX:-<none>} → $NEW_PREFIX"
        
        # 删除旧路由
        if [ -n "$OLD_PREFIX" ]; then
            log "删除旧路由: $OLD_PREFIX"
            ip -6 route del local "$OLD_PREFIX" dev "$DEV" 2>/dev/null || true
        fi
        
        # 添加新路由
        log "添加新路由: $NEW_PREFIX"
        ip -6 route add local "$NEW_PREFIX" dev "$DEV" 2>/dev/null || true
        
        # 更新 ndppd.conf
        if [ -n "$OLD_PREFIX" ]; then
            sed -i "s|$OLD_PREFIX|$NEW_PREFIX|g" "$CONF"
        else
            log "插入新的 rule 段到 ndppd.conf"
            sed -i "/proxy $DEV {/a\    rule $NEW_PREFIX {\n        static\n        iface $DEV\n    }" "$CONF"
        fi
        
        # 重启 ndppd
        log "重启 ndppd..."
        systemctl restart ndppd || log "⚠️ 无法重启 ndppd,请检查 systemd 服务状态。"
        
        # 更新 Xray 配置
        log "更新 Xray 配置..."
        update_xray_config "$NEW_PREFIX"
        
        log "✅ IPv6 前缀更新完成: $NEW_PREFIX"
        FIRST_RUN=false
    fi
    
    sleep 3
done
# /etc/systemd/system/update-ipv6-prefix.service
[Unit]
Description=IPv6 Prefix Monitor Service
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=/usr/local/bin/update-ipv6-prefix.sh
Restart=always
RestartSec=3
User=root

# 安全加固(可选)
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target

5. 食用方法

package main

import (
        "fmt"
        "github.com/imroc/req/v3"
        "time"
)

func main() {
        client := req.C().SetProxyURL("http://127.0.0.1:10808")

        ticker := time.NewTicker(time.Second)
        for range ticker.C {
                resp, err := client.R().
                        Get("http://api.bilibili.com/x/web-interface/zone")

                if err != nil {
                        panic(err)
                }

                fmt.Println(resp.String())
        }
}

0 条评论

发表回复

Avatar placeholder

您的邮箱地址不会被公开。 必填项已用 * 标注


这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理