如何使用 systemd-networkd 在 Linux 上使用 IPv6 互联网地址

如何使用 systemd-networkd 在 Linux 上使用 IPv6 互联网地址

我的提供商互联网路由器使用 IPv6 地址时出现了问题,我意识到我并不真正了解如何使用 IPv6 地址。为了了解情况,我喜欢问如何使用不同的 IPv6 地址类型,例如,在它们之间只有一个路由器的情况下将流量从一个主机路由到另一个主机。

Using three nodes:
┏━━━━━━━━━┓   addr: 2001:db8:0:10::1/64    addr: 2001.db8:0:20::1/64   ┏━━━━━━━━━┓
┃ host-a  ┃                      ╲ ┏━━━━━━━━━━┓ ╱                      ┃ host-b  ┃
┃     eth0┣════════════════════════┫eth0  eth1┣════════════════════════┫eth0     ┃
┗━━━━━━━━━┛ ╲                      ┃  router  ┃                      ╱ ┗━━━━━━━━━┛
  addr: 2001:db8:0:10::2/64        ┗━━━━━━━━━━┛         addr: 2001:db8:0:20::2/64
subnet: 2001:db8:0:10::/64                            subnet: 2001:db8:0:20::/64

我该如何设置它?

答案1

到目前为止,我还没有找到像这样的简单示例,可以通过参考资料来了解基础知识。这更像是一个概念证明,但我花了一些精力,所以我将逐步与社区分享。

准备工作

我在 KVM(基于内核的虚拟机)上使用三台虚拟机,全部使用 Debian 11(bullseye,当前为测试版本),并使用来自RFC 2460
节点是启用 IPv6 的接口。
路由器是转发未明确发送给它的 IPv6 数据包的任何节点。
主持人是任何非路由器的节点。

我们需要一些辅助程序,只要我们在节点上有互联网访问,就应该安装这些程序,然后再重新配置其网络接口以进行测试。在所有节点上安装radvdump,在路由器上安装radvd。确保已radvd禁用,否则会使测试混乱。我将使用它tcpdump来查看网络上发生的事情,因此将其安装在路由器上。在 Debian 上,我使用以下方法完成所有这些操作:

~$ sudo apt install radvdump        # on hosts and router
router ~$ sudo apt install radvd    # only on the router
router ~$ sudo systemctl disable --now radvd.service
router ~$ sudo apt install tcpdump  # only on the router

我使用 IPv6 地址前缀2001:DB8::/32保留用于文档 (RFC 3849) 可用于有效的全球单播地址,但不能路由到互联网。

为了方便使用,这里有一些规格:

有一些地址类型可用(RFC 4291

Unspecified          ::/128
Loopback             ::1/128
default route        ::/0
Multicast            FF00::/8
Unique Local unicast FD00::/8
Global Unicast       (everything else)
Global Anycast       (same as Global Unicast)   not used in this example

多播地址的范围(RFC 4291):

FF00::  reserved
FF01::  Interface-Local scope
FF02::  Link-Local scope
FF03::  reserved
FF04::  Admin-Local scope
FF05::  Site-Local scope
FF06:: to FF07::  (unassigned)
FF08::  Organization-Local scope
FF09:: to FF0D::  (unassigned)
FF0E::  Global scope
FF0F::  reserved

众所周知的 IPv6 多播地址(摘录 - 完整列表位于互联网号码分配机构):

ff02::1     all nodes
ff02::2     all routers
ff02::5     all OSPF (Open Shortest Path First) routers
ff02::6     all OSPF DRs (OSPF Designated Routers)
ff02::9     all RIP (Routing Information Protocol) routers
ff02::a     all EIGRP (Enhanced Interior Gateway Routing Protocol) routers
ff02::c     SSDP (Simple Service Discovery Protocol)
ff02::d     all PIM (Protocol Independent Multicast) routers
ff02::f     UPNP (Universal Plug and Play) devices
ff02::11    all homenet nodes
ff02::12    VRRP (Virtual Router Redundancy Protocol)
ff02::16    all MLDv2-capable routers
ff02::1a    all RPL (Routing Protocol for Low-Power and Lossy Networks) routers (used in Internet of Things (IoT) devices)
ff02::fb    multicast DNS IPv6
ff02::101   network time (NTP)
ff02::1:2   all DHCP agents
ff02::1:3   LLMNR (Link-Local Multicast Name Resolution)
ff02:0:0:0:0:1:ff00::/104   solicited node address
ff02:0:0:0:0:1-2:ff00::/104     node information query
ff05::1:3   all DHCP server (site)
ff05::101   all NTP server (site)

使能够systemd-networkd依照指示

部分快步使用 systemd-networkd 进行常规网络通信,然后回到这里。

我将禁用所有内容,以便我们可以看到需要什么,并逐步启用它。打开host-ahost-b使用此网络文件:

host-? ~$ sudo -Es   # if not already done
host-? ~# cat > /etc/systemd/network/04-wired.network <<EOF
[Match]
Name=eth0

[Network]
# on host-a uncomment
#Address=2001:db8:0:10::2/64
# on host-b uncomment
#Address=2001:db8:0:20::2/64
IPv6AcceptRA=no
LinkLocalAddressing=no
EOF

在路由器上使用这些:

router ~$ sudo -Es   # if not already done
router ~# cat > /etc/systemd/network/04-eth0.network <<EOF
[Match]
Name=eth0

[Network]
Address=2001:db8:0:10::1/64
IPv6AcceptRA=no
LinkLocalAddressing=no
EOF

router ~# cat > /etc/systemd/network/06-eth1.network <<EOF
[Match]
Name=eth1

[Network]
Address=2001:db8:0:20::1/64
IPv6AcceptRA=no
LinkLocalAddressing=no
EOF

简单的链路本地连接

首先,我将查看host-a和 之间的直接连接router。路由器已启动,我启动host-atcpdump子网 2001:db8:0:10/64 上显示:

host-a ~$ sudo tcpdump -n --number --interface=eth0 ip6 2>/dev/null
    1  23:25:28.211331 IP6 :: > ff02::16: HBH ICMP6, multicast listener report v2, 1 group record(s), length 28
    2  23:25:28.227326 IP6 :: > ff02::16: HBH ICMP6, multicast listener report v2, 2 group record(s), length 48
    3  23:25:28.671386 IP6 :: > ff02::16: HBH ICMP6, multicast listener report v2, 1 group record(s), length 28
    4  23:25:28.735354 IP6 :: > ff02::1:ff00:2: ICMP6, neighbor solicitation, who has 2001:db8:0:10::2, length 32
  • 包 1-3host-a作为侦听器加入多播组ff02::16- 所有支持 MLDv2 的路由器。路由器现在知道它想要接收路由消息。
  • 对于包 4,它询问是否有另一个节点的 IP 地址为 2001:db8:0:10::2。没有,因为没有回复。host-a可以使用该地址。

ping 至router工作区:

host-a ~$ ping6 -n 2001:db8:0:10::1
PING 2001:db8:0:10::1(2001:db8:0:10::1) 56 data bytes
64 bytes from 2001:db8:0:10::1: icmp_seq=1 ttl=64 time=0.829 ms
64 bytes from 2001:db8:0:10::1: icmp_seq=2 ttl=64 time=0.863 ms
64 bytes from 2001:db8:0:10::1: icmp_seq=3 ttl=64 time=0.858 ms
--- snip ---

链路本地单播地址

下一步我想连接到第二个接口eth1为此router,我们需要一条静态路由:

host-a ~$ sudo ip -6 route add 2001:db8:0:20::/64 via 2001:db8:0:10::1

ping6 -n 2001:db8:0:20::1不能稳定工作:

host-a ~$ ping6 -n 2001:db8:0:20::1
PING 2001:db8:0:20::1(2001:db8:0:20::1) 56 data bytes
64 bytes from 2001:db8:0:20::1: icmp_seq=1 ttl=64 time=0.881 ms
64 bytes from 2001:db8:0:20::1: icmp_seq=2 ttl=64 time=0.784 ms
64 bytes from 2001:db8:0:20::1: icmp_seq=3 ttl=64 time=0.898 ms
64 bytes from 2001:db8:0:20::1: icmp_seq=4 ttl=64 time=0.799 ms
64 bytes from 2001:db8:0:20::1: icmp_seq=5 ttl=64 time=0.805 ms
64 bytes from 2001:db8:0:20::1: icmp_seq=6 ttl=64 time=1.13 ms
64 bytes from 2001:db8:0:20::1: icmp_seq=7 ttl=64 time=0.795 ms
64 bytes from 2001:db8:0:20::1: icmp_seq=8 ttl=64 time=0.790 ms
64 bytes from 2001:db8:0:20::1: icmp_seq=9 ttl=64 time=0.793 ms
                                ^^^^^^^^^^
64 bytes from 2001:db8:0:20::1: icmp_seq=55 ttl=64 time=1025 ms
64 bytes from 2001:db8:0:20::1: icmp_seq=56 ttl=64 time=3.26 ms
64 bytes from 2001:db8:0:20::1: icmp_seq=57 ttl=64 time=0.793 ms
64 bytes from 2001:db8:0:20::1: icmp_seq=58 ttl=64 time=0.792 ms
64 bytes from 2001:db8:0:20::1: icmp_seq=59 ttl=64 time=0.789 ms
64 bytes from 2001:db8:0:20::1: icmp_seq=60 ttl=64 time=0.776 ms
64 bytes from 2001:db8:0:20::1: icmp_seq=61 ttl=64 time=0.803 ms
64 bytes from 2001:db8:0:20::1: icmp_seq=62 ttl=64 time=0.801 ms
64 bytes from 2001:db8:0:20::1: icmp_seq=63 ttl=64 time=0.774 ms
64 bytes from 2001:db8:0:20::1: icmp_seq=64 ttl=64 time=0.802 ms
                                ^^^^^^^^^^^
64 bytes from 2001:db8:0:20::1: icmp_seq=110 ttl=64 time=2049 ms
64 bytes from 2001:db8:0:20::1: icmp_seq=111 ttl=64 time=1025 ms
--- snip ---
^C
--- 2001:db8:0:20::1 ping statistics ---
170 packets transmitted, 35 received, 79.4118% packet loss, time 172432ms
rtt min/avg/max/mdev = 0.774/205.826/2048.843/536.218 ms, pipe 3

如果您跟踪该seq数字,您将看到 ping 延迟为 1 秒,大约 10 秒后中断,大约 45 秒后重复,数据包丢失率约为 80%。这不能称为稳定。我不太明白,但在查找故障排除时了解这种行为很有用。无论如何,它超出了规范,因为对于链路上的连接(2001:db8:0:10:2 到 2001:db8:0:10:1),必须使用链路本地地址指定于RFC 4291 - 链路本地 IPv6 单播地址

链路本地地址旨在用于单个链路上的寻址,以实现自动地址配置、邻居发现或不存在路由器等目的。

检查接口上的链路本地地址。没有:

host-a ~$ ip -6 -br addr
lo               UNKNOWN        ::1/128
eth0             UP             2001:db8:0:10::2/64

LinkLocalAddressing=ipv6我在所有节点的所有文件中启用它/etc/systemd/network/*.network,然后重新启动、检查并 ping:

host-a ~$ ip -6 -br addr
lo               UNKNOWN        ::1/128
eth0             UP             2001:db8:0:10::2/64 fe80::5054:ff:febc:adbe/64

router ~$ ip -6 -br addr
lo               UNKNOWN        ::1/128
eth0             UP             2001:db8:0:10::1/64 fe80::5054:ff:fe0f:194e/64
eth1             UP             2001:db8:0:20::1/64 fe80::5054:ff:fe0f:194e/64

host-b ~$ ip -6 -br addr
lo               UNKNOWN        ::1/128
eth0             UP             2001:db8:0:20::2/64 fe80::5054:ff:fe9b:34b9/64

host-a ~$ sudo ip -6 route add 2001:db8:0:20::/64 via 2001:db8:0:10::1
host-a ~$ ping6 -n 2001:db8:0:20::1
PING 2001:db8:0:20::1(2001:db8:0:20::1) 56 data bytes
64 bytes from 2001:db8:0:20::1: icmp_seq=9 ttl=64 time=2.08 ms
64 bytes from 2001:db8:0:20::1: icmp_seq=10 ttl=64 time=0.780 ms
64 bytes from 2001:db8:0:20::1: icmp_seq=11 ttl=64 time=0.783 ms
--- snip ---

作品。

静态路由

如果我尝试从 连接host-a到 ,host-bping6 -n 2001:db8:0:20::2失败。这是因为router不在其接口之间转发包。我们必须启用它。只需附加IPForward=ipv6*.network文件即可。我们还需要一个静态路由,host-b以便它知道将回复发送到哪里host-a。我们现在将持久化。因此您将获得以下.network文件:

主机a

host-a ~$ cat /etc/systemd/network/04-wired.network
[Match]
Name=eth0

[Network]
Address=2001:db8:0:10::2/64
IPv6AcceptRA=no
LinkLocalAddressing=ipv6

[Route]
Destination=2001:db8:0:20::/64
Gateway=2001:db8:0:10::1

路由器

router ~$ cat /etc/systemd/network/04-eth0.network
[Match]
Name=eth0

[Network]
Address=2001:0DB8:0:10::1/64
IPv6AcceptRA=no
LinkLocalAddressing=ipv6
IPForward=ipv6

router ~$ cat /etc/systemd/network/06-eth1.network
[Match]
Name=eth1

[Network]
Address=2001:0DB8:0:20::1/64
IPv6AcceptRA=no
LinkLocalAddressing=ipv6
IPForward=ipv6

主机-b

host-b ~$ cat /etc/systemd/network/04-wired.network
[Match]
Name=eth0

[Network]
Address=2001:db8:0:20::2/64
IPv6AcceptRA=no
LinkLocalAddressing=ipv6

[Route]
Destination=2001:db8:0:10::/64
Gateway=2001:db8:0:20::1

路由器通告

静态路由不太方便。这通常是自动完成的。主机可以向路由器询问其配置。要支持它,您必须IPv6AcceptRA=yes在其/etc/systemd/*.network文件中启用它。如果您这样做,您会在tcpdump启动主机时发现:

host-a ~$ sudo tcpdump -n --number --interface=eth0 ip6 2>/dev/null
 1  18:06:20.965014 IP6 :: > ff02::16: HBH ICMP6, multicast listener report v2, 1 group record(s), length 28
 2  18:06:20.976965 IP6 :: > ff02::16: HBH ICMP6, multicast listener report v2, 2 group record(s), length 48
 4  18:06:21.701148 IP6 :: > ff02::1:ffbc:adbe: ICMP6, neighbor solicitation, who has fe80::5054:ff:febc:adbe, length 32
 5  18:06:21.861153 IP6 :: > ff02::1:ff00:2: ICMP6, neighbor solicitation, who has 2001:db8:0:10::2, length 32
 7  18:06:22.725190 IP6 fe80::5054:ff:febc:adbe > ff02::16: HBH ICMP6, multicast listener report v2, 3 group record(s), length 68
 9  18:06:24.225649 IP6 fe80::5054:ff:febc:adbe > ff02::2: ICMP6, router solicitation, length 16
10  18:06:27.851217 IP6 fe80::5054:ff:febc:adbe > ff02::2: ICMP6, router solicitation, length 16
11  18:06:35.087287 IP6 fe80::5054:ff:febc:adbe > ff02::2: ICMP6, router solicitation, length 16
--- snip ---

除了已知的软件包之外,我们multicast listener report v2还找到了其他软件包。通过此消息,主机正在向路由器请求接口配置。但是路由器没有响应,因此主机继续询问。在 Linux 上,IPv6 路由器不会立即回复路由器请求。它需要额外的服务来管理这一点。我们已经安装了它,但已禁用它。使用 Debian 中的简单示例,我们现在配置并启用它:neighbor solicitationrouter solicitationrouterradvd/usr/share/doc/radvd/examples/simple-radvd.conf

router ~$ sudo -Es   # if not already done
router ~# cat > /etc/radvd.conf <<EOF
interface eth0
{
   AdvSendAdvert on;
   prefix 2001:db8:0:10::/64
   {
   };
};

interface eth1
{
   AdvSendAdvert on;
   prefix 2001:db8:0:20::/64
   {
   };
};
EOF

~# systemctl enable radvd.service

因为我们希望主机现在从路由器获取其配置,所以我们必须从其.network文件中删除手动设置。它们应该如下所示:

主机a主机-b

host-? ~$ cat /etc/systemd/network/04-eth0.network
[Match]
Name=eth0

[Network]
IPv6AcceptRA=yes
LinkLocalAddressing=ipv6

路由器

router ~$ cat /etc/systemd/network/04-eth0.network
[Match]
Name=eth0

[Network]
Address=2001:db8:0:10::1/64
IPv6AcceptRA=no
LinkLocalAddressing=ipv6
IPForward=ipv6

router ~$ cat /etc/systemd/network/06-eth1.network
[Match]
Name=eth1

[Network]
Address=2001:db8:0:20::1/64
IPv6AcceptRA=no
LinkLocalAddressing=ipv6
IPForward=ipv6

重新启动所有节点,您应该有一个自动配置的运行简单网络。

简化

为了说明需要什么,我明确地将选项写入了网络配置文件。但大多数都是默认设置,所以我们可以忽略它们。

主机a主机-b

host-? ~$ cat /etc/systemd/network/04-eth0.network
[Match]
Name=eth0

路由器

router ~$ cat /etc/systemd/network/04-eth0.network
[Match]
Name=eth0
[Network]
Address=2001:db8:0:10::1/64
IPForward=ipv6

router ~$ cat /etc/systemd/network/06-eth1.network
[Match]
Name=eth1
[Network]
Address=2001:db8:0:20::1/64
IPForward=ipv6

调试

您可能需要检查主机是否从路由器获取了正确的设置,或者是否存在路由器(例如来自您的互联网提供商),您必须知道它发送的具体设置。然后,您可以开始radvdump查看收到的路由器广告。如果启动它,请稍等片刻,直到收到路由器广告。这是使用默认设置的路由器广告router

host-a ~$ sudo radvdump
#
# radvd configuration generated by radvdump 2.18
# based on Router Advertisement from fe80::5054:ff:fe0f:194e
# received by interface eth0
#

interface eth0
{
        AdvSendAdvert on;
        # Note: {Min,Max}RtrAdvInterval cannot be obtained with radvdump
        AdvManagedFlag off;
        AdvOtherConfigFlag off;
        AdvReachableTime 0;
        AdvRetransTimer 0;
        AdvCurHopLimit 64;
        AdvDefaultLifetime 1800;
        AdvHomeAgentFlag off;
        AdvDefaultPreference medium;
        AdvSourceLLAddress on;

        prefix 2001:db8:0:10::/64
        {
                AdvValidLifetime 86400;
                AdvPreferredLifetime 14400;
                AdvOnLink on;
                AdvAutonomous on;
                AdvRouterAddr off;
        }; # End of prefix definition

}; # End of interface definition

名称解析

只有使用 DNS 服务器才能在整个网络上进行名称解析。它的设置是一个新问题,超出了本文的范围。在这个简单的网络上没有可用的 DNS 服务器也是我们总是使用带有选项的 ping6 的原因,-n该选项会抑制查询 DNS 服务器来转换 IP 地址。如果没有-n,ping6 会尝试为每个请求连接到 DNS 服务器,并且会超时。这会大大减慢 ping 速度并且无法使用。

对于本地链接,启用多播DNS没有问题(移动DNS)以便您能够对直接连接的节点进行名称解析。只需添加选项

MulticastDNS=yes

[Network]每个节点/etc/systemd/network/*.network文件的部分。然后你应该能够

host-a ~$ ping6 -n router.local
PING router.local(fe80::5054:ff:fe0f:194e%eth0) 56 data bytes
64 bytes from fe80::5054:ff:fe0f:194e%eth0: icmp_seq=1 ttl=64 time=2.37 ms
64 bytes from fe80::5054:ff:fe0f:194e%eth0: icmp_seq=2 ttl=64 time=0.799 ms
64 bytes from fe80::5054:ff:fe0f:194e%eth0: icmp_seq=3 ttl=64 time=0.823 ms
--- snip ---

ping6 -n host-b.local不会起作用,host-a因为移动DNS是不可路由的。


参考:

相关内容