前言

Leko 其實一直都在煩惱,有沒有甚麼 VPN 可以支援 L2 讓我能發 SLAAC 的 :(

昨天和友人在聊天的時候,對方正在用 OpenVPN 打 Tunnel,我靈光一現突然想到,ZeroTier 不也是 L2 的嗎?

試了一下果真成功了,就在這邊紀錄一下。

環境

Leko 的環境如下:

  • RouterOS VM (發放 DHCP-PD 給有 ZT 的 CT)
  • Ubuntu 18.04 CT (ZeroTier Host,負責發 SLAAC)

環境非常自由,前二者只要能達到括號內的需求應該都可以,甚至可以放在同一台。

CT 網路說明

網路卡 int0 負責上網 (接入公網),這裡我的配置是帶有 UPnP 的 IPv4 NAT。
(UPnP 讓 ZeroTier 可以自動穿透)

網路卡 eth0 用來接收 RouterOS 發放的 DHCP-PD。

步驟

以下操作都假設在 root 下進行。

因為 Ubuntu 預設有一個 systemd-resolved 負責解析 Hostname,占用 53 Port 會影響我們後續操作,因此我們先把它關掉:

1
2
root@CT:~# systemctl disable systemd-resolved
root@CT:~# systemctl stop systemd-resolved

然後我們拿一些必要套件。

1
2
root@CT:~# apt update && apt install curl wide-dhcpv6-client gnupg2 dnsmasq
root@CT:~# curl -s https://install.zerotier.com | bash

接著加入我們的 ZeroTier Network:

1
root@CT:~# zerotier-cli join [ID]

然後把新增的網路卡名稱記著,之後會用到:

1
root@CT:~# ip a

接著開啟轉發:

1
2
root@CT:~# nano /etc/sysctl.conf
root@CT:~# sysctl -p
1
2
3
4
5
6
7
8
9
10
# /etc/sysctl.conf

net.ipv4.conf.all.forwarding = 1
net.ipv6.conf.all.disable_ipv6 = 0
net.ipv6.conf.default.disable_ipv6 = 0
net.ipv6.conf.lo.disable_ipv6 = 0
net.ipv6.conf.default.forwarding = 1
net.ipv6.conf.all.forwarding = 1
net.ipv6.conf.all.proxy_ndp = 1
net.ipv6.conf.all.accept_ra = 2

然後設定 DHCPv6-PD 並重新啟動,注意把 config 內的 ztnfapdryf 換成自己的 ZT 網卡名稱:

1
2
3
4
root@CT:~# nano /etc/wide-dhcpv6/dhcp6c.conf
root@CT:~# nano /etc/systemd/system/wide-dhcpv6.service
root@CT:~# systemctl enable wide-dhcpv6.service
root@CT:~# reboot
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# /etc/wide-dhcpv6/dhcp6c.conf

interface eth0 {
# request a non-temporary address
# request prefix delegation address
send ia-pd 1;
request domain-name-servers;
request domain-name;
};

id-assoc pd 1 {
prefix ::/64 infinity;
prefix-interface ztnfapdryf {
sla-len 0;

# elated with ia-pd to configure subnet on lan interface - eth1
sla-id 1;

# "postfix" of IPv6 address on lan interface.
# if not configured interface will has EUI-64 (mac based) address.
# In this case, eth1 will get a IPv6 ending with ::1
ifid 1;
};
};
1
2
3
4
5
6
7
8
9
10
11
12
13
# /etc/systemd/system/wide-dhcpv6.service

[Unit]
Description=wide-dhcpv6
Before=dnsmasq.service
After=network.target ypbind.service nfs.service nfsserver.service rpcbind.service SuSEfirewall2_init.service

[Service]
ExecStart=/usr/sbin/dhcp6c -c /etc/wide-dhcpv6/dhcp6c.conf -P default -d -D -f eth0
RemainAfterExit=true

[Install]
WantedBy=multi-user.target

重新啟動後,應該可以看到 ZeroTier 的網卡上多了一個 [Prefix]::1 的 IP。

接著我們設定 SLAAC 和 RA,注意把 config 內的 ztnfapdryf 換成自己的 ZT 網卡名稱:

1
2
3
root@CT:~# nano /etc/dnsmasq.conf
root@CT:~# systemctl enable dnsmasq.service
root@CT:~# systemctl restart dnsmasq.service
1
2
3
4
5
6
7
8
9
10
11
# /etc/dnsmasq.conf

except-interface=eth0
bogus-priv
enable-ra

# Create a IPv6 range from address on the interface. The ::1 is related to the ifid in /etc/wide-dhcpv6/dhcp6c.conf.
dhcp-range=tag:ztnfapdryf,::1,constructor:ztnfapdryf, ra-names, 12h

# ra-names - it gives DNS names to IPv6 hosts
local=/lan/

然後我們新增預設路由並讓他開機自動啟動:

1
2
root@CT:~# nano route.sh
root@CT:~# nano /etc/init/route.conf

請將 fe80::908c:eeff:fe18:5c56 換成上游的 IP,ztnfapdryf 換成 ZT 的網卡名稱:

1
2
3
4
5
6
7
#!/bin/sh

# route.sh
ip -6 r add default via fe80::908c:eeff:fe18:5c56 dev eth0
ip6tables -A FORWARD -m state --state NEW -i ztnfapdryf -o eth0 -j ACCEPT
ip6tables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
ip6tables -P FORWARD DROP

接著在 ZT 內新增一段預設路由:

其中 10.147.18.45 是我的 CT IP,記得換成你的哦。

然後 ZT 加入其他 Peer 的時候記得開啟 Allow Default Route 跟 Allow Global,應該就能拿到 IPv6 了。

yay

附上完整的指令列表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/bin/sh

systemctl disable systemd-resolved
systemctl stop systemd-resolved

apt update
apt install curl wide-dhcpv6-client gnupg2 dnsmasq
curl -s https://install.zerotier.com | bash

zerotier-cli join [ID]
# ztnfapdryf

nano /etc/sysctl.conf
sysctl -p

nano /etc/wide-dhcpv6/dhcp6c.conf
nano /etc/systemd/system/wide-dhcpv6.service
systemctl enable wide-dhcpv6.service
reboot

nano /etc/dnsmasq.conf
systemctl enable dnsmasq.service
systemctl restart dnsmasq.service

nano route.sh
nano /etc/init/route.conf