X86 软路由配置 IPv6 踩坑小记

2020-11-11 15:29:53 +08:00
 xarthur

原文连接

背景故事

这一次踩坑之旅的起源是一段来自内核恐慌 Telegram 群的关于 IPv6 的讨论,Rio 发了关于他配置 IPv6 时候的踩坑帖子,而我正好一直想把家里软路由的 IPv6 配置起来,就有了这一次经历。这里非常感谢 Rio 和听众群的朋友,没有他们的帮助也就没有这次的经历。

关于 IPv6 的小介绍

在开始配置环境之前,我想先做一个关于 IPv6 的小介绍。介绍一下之后会涉及到的一些概念,比如:RA,slaac 等。这个介绍不会涉及到 IPv6 整体是怎么工作的,主要介绍一下在 IPv6 设备是如何获取 IPv6 地址的。

在开始介绍在 IPv6 环境之前,得先介绍一下什么是 RA 。RA 也就是:Router Advertisement (路由器通告报文)是一种 ICMPv6 报文,ICMP 也就是我们日常 Ping 命令使用的报文。在 IPv6 点环境中路由发出的 RA 会携带一系列的信息告知设备如何配置自己的 IP 地址。

在 IPv6 中有多种自动配置 IP 的方式,这里我们只会接触到 slaac 和 DHCPv6,下面有个关于这两种方式区别的解释。

其中“自动配置”根据获取方式,又分为

▷ 无状态( Stateless ):根据路由通告报文 RA ( Router Advertisement )包含的 prefix 前缀信息自动配置 IPv6 地址,组成方式是 Prefix + (EUI64 or 随机)。Stateless 也可以称为 SLAAC ( Stateless address autoconfiguration )

▷ 有状态( Stateful ):通过 DHCPv6 方式获得 IPv6 地址

——IPv6 系列-详解自动分配 IPv6 地址

因为安卓设备只支持 slaac,所以我使用了 slaac 方式配置局域网内设备的 IP 。

环境

我这里使用系统是:

Linux version 4.19.0-9-amd64 (debian-kernel@lists.debian.org) (gcc version 8.3.0 (Debian 8.3.0-6)) #1 SMP Debian 4.19.118-2+deb10u1 (2020-06-07)

机器本身是一台四个网口的软路由。

上网方式是 PPPoE 拨号上网

环境配置

首先配置网卡。

/etc/network/interfaces

source /etc/network/interfaces.d/*

# 本地接口
auto lo
iface lo inet loopback

# 广域网接口
allow-hotplug enp1s0

# 局域网接口
auto br0
allow-hotplug br0
iface br0 inet static
      address 192.168.2.1
      network 192.168.2.0
      netmask 255.255.255.0
      brocast 192.168.2.255
      bridge-ports enp2s0 enp3s0 enp4s0
      
# PPPoE 接口,由 pppoeconf 自动生成
auto dsl-provider
iface dsl-provider inet ppp
pre-up /bin/ip link set enp1s0 up # line maintained by pppoeconf
provider dsl-provider
iface enp1s0 inet manual

这里是我的的软路由的接口配置,可以看到出口网卡是 enp1s0,我会通过这个网卡进行 PPPoE 拨号上网。这个配置最后的 dsl-provider 是由 pppoeconf 自动生成的,我们之后会讲到。

请注意,在这里不要配置 DHCP 连接,不然内置的 dhclient 会和之后我们用到的 wide-dhcpv6-client 冲突。

因为我们是要配置软路由,所以我们需要启用 IPv6 转发。

/etc/sysctl.conf 添加上:

net.ipv6.conf.all.forwarding=2
net.ipv6.conf.default.forwarding=2

net.ipv6.conf.all.accept_ra=2
net.ipv6.conf.default.accept_ra=2

net.ipv6.conf.all.use_tempaddr=2
net.ipv6.conf.default.use_tempaddr=2

我们通过设置: net.ipv6.conf.all.forwarding=2net.ipv6.conf.default.forwarding=2 启用了 IPv6 转发,但是根据注释:

Uncomment the next line to enable packet forwarding for IPv6

Enabling this option disables Stateless Address Autoconfiguration

based on Router Advertisements for this host

开启了这个选项之后,系统将不会进行 RA 处理,也就是我们的广域网将不会有 IPv6 地址,所以我们这里手动设置了:net.ipv6.conf.all.accept_ra=2net.ipv6.conf.default.accept_ra=2 来启用 RA 处理。

最后两行是启用 IPv6 的隐私扩展,具体可以阅读 Arch Wiki 的相关介绍

因为 RA 是 ICMPv6 报文,所以我们要在防火墙上允许 ICMPv6 的通过。

iptables -A INPUT -p ipv6-icmp -j ACCEPT
iptables -A FORWARD -p ipv6-icmp -j ACCEPT
iptables -A OUTPUT -p ipv6-icmp -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp -j ACCEPT
ip6tables -A FORWARD -p ipv6-icmp -j ACCEPT
ip6tables -A OUTPUT -p ipv6-icmp -j ACCEPT

接下来我们来配置 PPPoE 上网,运行:

apt install pppoeconf

在安装 pppoeconf 的时候会自动安装 pppdpppoeconf 是个帮助你配置 pppd 的小工具,安装完成后输入:

pppoeconf

然后按照指示输入你宽带的账号密码,如果其他选项的含义不清楚请选择默认,在配置成功后,你可以通过: poffponplog,来关闭、开启 PPoE 以及显示 log 。

但是在默认的情况下,pppoeconf 自动生成的配置文件不会启用 IPv6,我们还需要对配置文件进行一些修改。

配置文件在 /etc/ppp/peers/ 目录下,我这里自动生成的是 dsl-provider

/etc/ppp/peers/dsl-provider

noipdefault
defaultroute
replacedefaultroute
hide-password
noauth
persist
persist
maxfail 0
plugin rp-pppoe.so
nic-enp1s0
user "宽带账号"
usepeerdns
+ipv6
debug

这里我在末尾添加上了+ipv6,请注意加号,其次我还添加上了 debug,用于之后使用 plog 来 Debug 问题,此时你重新启动 PPPoE,然后输入 ip addr show ppp0,观察 ppp0 接口应该就能看到分配的 IPv6 地址了,因为我们启用了隐私扩展,所以你能看到有两个 IPv6 地址。

接下来我们需要给内网设备也分配对应的 IPv6 地址。这里我们用到了 Prefix delegation (前缀代理),简称 PD 。简单来说就是我们向我们的上级路由发送 PD 请求,上级路由会分给我们一个前缀长度小于等于 64 的网段,然后我们就能将个网段划分成一个或者一些 /64 的网段接着向局域网内的设备分配,此时局域网内的设备的上级路由就是我们的网关。

这里有个需要注意的地方,我们向局域网设备分配的 IP 地址也是公网地址,而不是 IPv4 时代的私有地址,不过因为上级路由是我们的网关,所以这些设备其实是在一个局域网内,并且因为这些地址都是公网地址,所以我们不需要做 NAT 转化的操作。

为了实现这个功能,我们需要使用 wide-dhcpv6-client

首先安装:

apt install wide-dhcpv6-client

在安装完成后,我们需要配置 PD 。

编辑:/etc/wide-dhcpv6/dhcp6c.conf

interface ppp0 {
  send ia-pd 0;
};
id-assoc pd 0 {
  # use the interface connected to your LAN
  prefix-interface br0 {
    sla-id 1;
    sla-len 4;
  };
};

这段配置也是来自 Archi Wiki,其中的 ppp0 应该是你广域网的接口,而 br0 应该是你局域网的接口,关于 sla-len的长度有个注释需要注意:

注意: sla-len 应设置为满足 (WAN-prefix) + (sla-len) = 64 的值。这里示范的情况是针对一个长度 /56 的前缀,56+8=64 。对于前缀长度 /64 的网络,sla-len 应为 0

因为我的 ISP 分配的是个 /60 的网段,所以 sla-len 的值是 4,我建议大家可以先填成 0,然后通过运行:dhcp6c -f -D ppp0 命令,观察你的 ISP 分配的网段大小,然后再修改对应的值。

这里非常感谢 Rio 提供的一个新的现代化的 DHCP6c System Service 用来替换自带的 wide-dhcpv6-client.service

添加到 /etc/systemd/system/dhcp6c.service

[Unit]
Description=WIDE DHCPv6 Client
Wants=network-online.target
After=network-online.target

[Service]
ExecStart=/usr/sbin/dhcp6c -f ppp0
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=3
NoNewPrivileges=yes
PrivateTmp=yes
ProtectHome=yes
ProtectSystem=strict
ReadWritePaths=/run/ /var/log/
ProtectKernelTunables=yes
ProtectControlGroups=yes
SystemCallFilter=~@mount
SystemCallArchitectures=native
LockPersonality=yes
MemoryDenyWriteExecute=yes
RestrictRealtime=yes
RemoveIPC=yes

[Install]
WantedBy=multi-user.target

然后运行:

systemctl stop wide-dhcpv6-client.service
systemctl disable wide-dhcpv6-client.service
systemctl enable dhcp6c.service

你就可以通过 dhcp6c.service 来控制 wide-dhcpv6-client 了。

运行成功后,观察你局域网的接口,应该就能看到对应分配的地址了。

最后需要向局域网设备发送 RA,使用 slaac 来分配 IP 地址,这里我们使用了 Dnsmasq,因为 Dnsmasq 是个非常常用的软件,就不多介绍了,在 Dnsmasq 的配置文件里加上:

enable-ra
dhcp-range=::,constructor:br0,ra-only,slaac

br0 填入入你的局域网接口。

这时你的局域网设备应该也能分配到全球唯一的 IPv6 地址了。

6732 次点击
所在节点    宽带症候群
20 条回复
AoTmmy
2020-11-11 15:43:52 +08:00
好家伙写的真详细,不过 x86 的 op 直接把 IPv6 打开就能用了👀
xarthur
2020-11-11 15:49:49 +08:00
@AoTmmy 用 OpenWRT 没有完整的 Linux 发行版灵活(逃
0987363
2020-11-11 15:54:17 +08:00
v6 好些插件支持有问题,我都是只用 v4.。。。
xtx
2020-11-11 16:04:16 +08:00
图都挂
xarthur
2020-11-11 19:08:36 +08:00
@xtx 我这里没什么问题,不过挂了也不影响文章就是了。
jousca
2020-11-11 19:38:41 +08:00
图我观察正常。挂了是什么操作?
leeyuky
2020-11-11 20:45:32 +08:00
看到这帖子只想说一句 windows server 自带的 hyper-v 里面的 virtual switch 对 ipv6 支持有问题,当时折腾了好久才搞好
tankren
2020-11-11 22:13:06 +08:00
pfsense 路过
brMu
2020-11-11 22:59:30 +08:00
讲真,我之前也是拿 debian 9 来折腾的,也是遇到各种坑,最后也是成功了,不过再后还是放弃了用 debian9 来拨号,直接用爱快来拨号和分流,debian 做为二级路由来实现各种功能。
brMu
2020-11-11 23:04:14 +08:00
补充一下,原因很简单,爱快做为路由,拨号,分流,DDNS,限速,UPNP,NAT,已经非常的完善了,自己折腾实在太费心,用 debian 做为二级路由,也可以实现所有想要的功能,可以专心在这上面。
shikkoku
2020-11-12 12:19:40 +08:00
有点复杂,不知道如何实现 full cone 。
qbqbqbqb
2020-11-12 14:38:52 +08:00
@shikkoku Full cone 是针对 ipv4 有 NAT 的情况的。ipv6 没有 NAT,都是公网地址,只要在 iptables 防火墙的 FORWARD chain 放行相应端口就行了
cwbsw
2020-11-12 15:02:13 +08:00
@qbqbqbqb FaceTime 这类 VOIP 应用有服务器帮助联机的话,不需要防火墙放行端口的。
xarthur
2020-11-12 15:21:54 +08:00
@shikkoku 不需要 full cone,在 IPv6 里没有 NAT 了
IPv4:
|
网关: 192.168.1.1 and 154.233.233.233 ( 192.168.1.1 -> 154.233.233.233 , 需要进行 NAT )
/ \
/ \
/ \
设备 1 设备 2
192.168.1.2 192.168.1.3

IPv6:
|
网关 : 2408:8256:3075:BEEF::1
/ \
/ \
/ \
/ \
/ \
/ \
/ \
设备 1 设备 2
2408:8256:3075:BEEF::2 2408:8256:3075:BEEF::2

你可以看到这里是在 IPv6 的网络里,你不需要进行地址转换,因为所以的设备都有公网 IP,所谓的「局域网」说明的是这些设备的上层路由是你的网关设备。
feast
2020-11-13 13:49:24 +08:00
讲半天我感觉你还是在把 SLAAC 和 DHCP6 混用
szdosar
2020-11-13 14:50:14 +08:00
我想知道,有公网的 IPv6 后,如何利用?比如,如何远程访问我的软路由。
xarthur
2020-11-13 20:13:20 +08:00
@szdosar 如果你的软路由已经被分配到 IPv6 的公网 IP,那么你只需在你的网关哪里,打开防火墙放行就行。
xarthur
2020-11-13 20:13:41 +08:00
*那里
DopaminePlz
2020-11-13 23:09:05 +08:00
前段时间也用 OMV 折腾过 IPv6,结果失败了。现在看到楼主的文章,收藏一下先。不过我想以后应该折腾不动了。
题外话,原来只能分配的是 /64 子网,上周给换了一只猫,还是继续路由器拨号,结果变成 /60 了。
qbqbqbqb
2020-12-02 14:05:37 +08:00
@feast SLAAC 和 DHCPv6 本来在某种意义上就是需要“混用”的

一方面,家庭宽带都没有固定地址固定前缀,哪怕你局域网里准备只用 SLAAC,广域网端还是得用 DHCPv6 client 从运营商那边通过 PD 协议获取可用的地址段

另一方面,局域网端 DHCPv6 也是必须和 RA 配合使用才能起到效果的。
stateless 模式可以只用 RA,也可以用 DHCPv6 单独负责分发 DNS 等其它配置信息(因为 RA 一开始只支持配置地址和路由,不支持这些,后来支持通过 RDNSS 扩展分配 DNS 但兼容性不好)。这时候 RA 数据包里有一个“O”标记会设置为 1,指示客户端通过 SLAAC 配置完地址之后还需要通过 DHCPv6 配置 DNS 等其它信息。
stateful 模式虽然是由 DHCPv6 配置地址,但是有一些关键信息,比如默认路由和前缀长度(相当于 ipv4 里子网掩码的作用)都还是必须通过 RA 获得的。这时候 RA 数据包里有一个“M”标记会设置为 1 。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/724060

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX