给你的VPS添加无限个IPv6地址:基于systemd-networkd的优雅方案

By | 2026年6月9日

背景

在云服务中,IPv6地址往往比IPv4更充裕,但许多VPS提供商默认只分配一个IPv6地址。当你需要为爬虫、邮件服务或容器分配独立IPv6地址时,手动添加地址不仅繁琐,还可能遇到内核限制。本文将介绍一种基于systemd-networkd的优雅方案,让你轻松添加无限个IPv6地址,且重启后自动生效。

原理

传统方式使用ip addr add添加IPv6地址,但每个网卡有数量限制(通常为64个)。通过systemd-networkd,我们可以利用NDP代理(Neighbor Discovery Proxy)地址生成模式,让内核自动管理地址池,突破数量限制。核心思路是:

  1. 将网卡配置为接受多个IPv6地址,并启用NDP代理。
  2. 使用ip route添加路由,让子网内所有地址都可达。
  3. 通过脚本或服务动态添加地址,或利用Address指令批量配置。

前提条件

  • VPS运行Linux(推荐Debian/Ubuntu或CentOS 8+)
  • systemd版本≥240(大多数现代发行版满足)
  • VPS已分配至少一个IPv6地址和子网(例如2001:db8::/64
  • root权限

步骤一:确认当前IPv6配置

首先,查看你的IPv6地址和网关:


ip -6 addr show
ip -6 route show default

假设你的主网卡为eth0,IPv6地址为2001:db8::1/64,网关为fe80::1

步骤二:配置systemd-networkd

systemd-networkd是systemd的网络管理守护进程,默认可能未启用。我们将其配置为管理eth0

1. 启用systemd-networkd


systemctl enable systemd-networkd
systemctl start systemd-networkd

2. 创建网络配置文件

/etc/systemd/network/下创建文件20-eth0.network


[Match]
Name=eth0

[Network]
DHCP=no
IPv6AcceptRA=no
BindCarrier=eth0

[Address]
Address=2001:db8::1/64

[Route]
Destination=::/0
Gateway=fe80::1

[IPv6SendRA]
# 启用NDP代理,使子网内地址可达
ProxyNDP=yes

注意ProxyNDP=yes是关键,它让系统响应子网内所有地址的邻居请求,即使这些地址没有配置在网卡上。

3. 重启networkd


systemctl restart systemd-networkd

步骤三:批量添加IPv6地址

现在,我们可以通过一个脚本动态添加大量IPv6地址。以下脚本生成从2001:db8::22001:db8::1000的地址:


#!/bin/bash
# add-ipv6.sh

SUBNET="2001:db8::"
START=2
END=4096  # 可根据需要调整,但不要超过子网大小

for i in $(seq $START $END); do
    ADDR=$(printf "$SUBNET%x" $i)
    ip addr add $ADDR/64 dev eth0
    # 可选:添加路由,确保流量进入
    # ip route add $ADDR/128 dev eth0
done

运行脚本:


chmod +x add-ipv6.sh
./add-ipv6.sh

注意:直接使用ip addr add添加大量地址可能触发内核限制(最大约64个)。此时,NDP代理的作用就显现了:即使地址没有显式添加,只要路由可达,系统也能响应邻居请求。因此,我们实际上不需要添加所有地址,只需添加路由即可。

步骤四:使用路由代替地址(推荐)

更优雅的方式是只添加一条路由,让整个子网都可达:


ip route add 2001:db8::/64 dev eth0

但这样所有地址都会指向eth0,如果VPS提供商实施了反向路径过滤(rpfilter),可能会丢弃数据包。我们需要调整rpfilter:


sysctl -w net.ipv6.conf.eth0.rp_filter=2  # 宽松模式

持久化:在/etc/sysctl.d/90-ipv6.conf中添加:


net.ipv6.conf.eth0.rp_filter=2
net.ipv6.conf.all.rp_filter=2

然后应用:


sysctl -p /etc/sysctl.d/90-ipv6.conf

步骤五:动态分配与回收

如果你需要为每个服务分配独立地址,可以编写一个守护进程,监听请求并从地址池中分配。这里提供一个简单的bash函数:


# 分配一个地址
function assign_ipv6() {
    local pool_file="/var/ipv6_pool.txt"
    local used_file="/var/ipv6_used.txt"
    local subnet="2001:db8::"
    local next=$(cat $pool_file 2>/dev/null || echo 2)
    # 查找未使用的地址
    while ip addr show | grep -q "${subnet}$(printf '%x' $next)"; do
        ((next++))
    done
    local addr="${subnet}$(printf '%x' $next)/64"
    ip addr add $addr dev eth0
    echo $next > $pool_file
    echo $addr >> $used_file
    echo $addr
}

回收地址:


function release_ipv6() {
    local addr=$1
    ip addr del $addr dev eth0
    # 从used_file中删除
    sed -i "\|$addr|d" /var/ipv6_used.txt
}

常见坑与解决

坑1:NDP代理无效

如果配置了ProxyNDP=yes但其他主机无法ping通地址,检查内核参数:


sysctl net.ipv6.conf.eth0.proxy_ndp

如果为0,手动开启:


sysctl -w net.ipv6.conf.eth0.proxy_ndp=1

坑2:地址冲突

多个服务使用相同地址会导致路由混乱。务必使用状态管理,如上面脚本中的used_file

坑3:重启后地址丢失

使用systemd-networkd配置后,静态地址会保留。但动态添加的地址会丢失。解决方案:将分配脚本作为systemd服务运行,或使用ip addr add命令写入/etc/rc.local

更好的做法:利用systemd-networkd的[Address]段支持多个Address指令,但数量有限。对于大量地址,建议在[Network]下添加Address=2001:db8::/64,然后通过路由管理。

总结

通过systemd-networkd的NDP代理和路由配置,你可以轻松实现VPS上无限个IPv6地址的添加。核心要点:

  1. 启用NDP代理,让子网内所有地址可达。
  2. 使用路由而非显式地址,突破内核限制。
  3. 调整rp_filter避免丢包。
  4. 编写脚本管理地址池,实现动态分配。

这种方法不仅简化了管理,还提高了灵活性,适用于大规模IPv6地址需求场景。

延伸阅读

希望这篇教程对你有所帮助!如果你有更好的方案,欢迎在评论区交流。

发表回复

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