V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
xarthur
V2EX  ›  宽带症候群

X86 软路由配置 IPv6 踩坑小记

  •  3
     
  •   xarthur · 2020-11-11 15:29:53 +08:00 · 7221 次点击
    这是一个创建于 1473 天前的主题,其中的信息可能已经有所发展或是发生改变。

    原文连接

    背景故事

    这一次踩坑之旅的起源是一段来自内核恐慌 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 ([email protected]) (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 地址。

    ppp0

    接下来我们需要给内网设备也分配对应的 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 了。

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

    lan

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

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

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

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

    第 1 条附言  ·  2020-11-11 19:44:28 +08:00
    * `br0` 填入你的局域网接口。

    这时你的局域网设备应该也能分配到全球唯一的 IPv6 地址了。
    21 条回复    2024-05-21 20:14:52 +08:00
    AoTmmy
        1
    AoTmmy  
       2020-11-11 15:43:52 +08:00 via Android
    好家伙写的真详细,不过 x86 的 op 直接把 IPv6 打开就能用了👀
    xarthur
        2
    xarthur  
    OP
       2020-11-11 15:49:49 +08:00
    @AoTmmy 用 OpenWRT 没有完整的 Linux 发行版灵活(逃
    0987363
        3
    0987363  
       2020-11-11 15:54:17 +08:00
    v6 好些插件支持有问题,我都是只用 v4.。。。
    xtx
        4
    xtx  
       2020-11-11 16:04:16 +08:00
    图都挂
    xarthur
        5
    xarthur  
    OP
       2020-11-11 19:08:36 +08:00
    @xtx 我这里没什么问题,不过挂了也不影响文章就是了。
    jousca
        6
    jousca  
       2020-11-11 19:38:41 +08:00
    图我观察正常。挂了是什么操作?
    leeyuky
        7
    leeyuky  
       2020-11-11 20:45:32 +08:00
    看到这帖子只想说一句 windows server 自带的 hyper-v 里面的 virtual switch 对 ipv6 支持有问题,当时折腾了好久才搞好
    tankren
        8
    tankren  
       2020-11-11 22:13:06 +08:00
    pfsense 路过
    brMu
        9
    brMu  
       2020-11-11 22:59:30 +08:00
    讲真,我之前也是拿 debian 9 来折腾的,也是遇到各种坑,最后也是成功了,不过再后还是放弃了用 debian9 来拨号,直接用爱快来拨号和分流,debian 做为二级路由来实现各种功能。
    brMu
        10
    brMu  
       2020-11-11 23:04:14 +08:00
    补充一下,原因很简单,爱快做为路由,拨号,分流,DDNS,限速,UPNP,NAT,已经非常的完善了,自己折腾实在太费心,用 debian 做为二级路由,也可以实现所有想要的功能,可以专心在这上面。
    shikkoku
        11
    shikkoku  
       2020-11-12 12:19:40 +08:00
    有点复杂,不知道如何实现 full cone 。
    qbqbqbqb
        12
    qbqbqbqb  
       2020-11-12 14:38:52 +08:00
    @shikkoku Full cone 是针对 ipv4 有 NAT 的情况的。ipv6 没有 NAT,都是公网地址,只要在 iptables 防火墙的 FORWARD chain 放行相应端口就行了
    cwbsw
        13
    cwbsw  
       2020-11-12 15:02:13 +08:00
    @qbqbqbqb FaceTime 这类 VOIP 应用有服务器帮助联机的话,不需要防火墙放行端口的。
    xarthur
        14
    xarthur  
    OP
       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
        15
    feast  
       2020-11-13 13:49:24 +08:00
    讲半天我感觉你还是在把 SLAAC 和 DHCP6 混用
    szdosar
        16
    szdosar  
       2020-11-13 14:50:14 +08:00
    我想知道,有公网的 IPv6 后,如何利用?比如,如何远程访问我的软路由。
    xarthur
        17
    xarthur  
    OP
       2020-11-13 20:13:20 +08:00
    @szdosar 如果你的软路由已经被分配到 IPv6 的公网 IP,那么你只需在你的网关哪里,打开防火墙放行就行。
    xarthur
        18
    xarthur  
    OP
       2020-11-13 20:13:41 +08:00
    *那里
    DopaminePlz
        19
    DopaminePlz  
       2020-11-13 23:09:05 +08:00 via Android
    前段时间也用 OMV 折腾过 IPv6,结果失败了。现在看到楼主的文章,收藏一下先。不过我想以后应该折腾不动了。
    题外话,原来只能分配的是 /64 子网,上周给换了一只猫,还是继续路由器拨号,结果变成 /60 了。
    qbqbqbqb
        20
    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 。
    uvhchina
        21
    uvhchina  
       186 天前
    这个 dhcp6c.service 不是很好用,断线了或者重连了会不更新本地 br0 的 v6 地址,我是按照这个来的 https://blog.bling.moe/post/3/,在 ppp.up.d 和 ppp.down.d 里面加脚本解决的
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1045 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 23:20 · PVG 07:20 · LAX 15:20 · JFK 18:20
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.