NAT64/DNS64の環境をUbuntu Server 18.04で構築する

# 概略

IPv6の勉強のため、NAT64/DNS64でインターネット接続性のあるIPv6 onlyなネットワークを作ろうとしたところ、バージョンによる違いが発生してつまづきまくった(特にJoolとisc-dhcp-server)。

とりあえず、現時点で最新のUbuntu 18.04 ServerとJool 4.0.0を使ったNAT64/DNS64環境の構築方法をまとめた。

# ネットワーク構成

~~~
    +-----+
    | GW  | 192.168.10.253
    +--+--+
       |
       |eth0: 192.168.10.217/24
+------+------+
|   Host A    |
+------+------+
       |eth1: fc01:0:0:1::1/64
       |
       |eth0: DHCPv6で付与されたアドレス
+------+------+
|   Host B    |
+-------------+
~~~

* Host A
    * 本記事の主役
    * NAT64, DNS64, DHCPv6サーバ
    * OS: Ubuntu Server 18.04.2
    * eth0がIPv4ネットワークに、eth1がIPv6ネットワークに接続
* Host B
    * IPv6ネットワークのテスト端末
    * OS: Ubuntu Server 18.04.2

Host Aはnetplanで下記設定になればOK。

~~~:/etc/netplan/99_config.yaml 
network:
    ethernets:
        eth0:
            addresses:
            - 192.168.10.217/24
            gateway4: 192.168.10.253
            nameservers:
                addresses:
                - 8.8.8.8
        eth1:
            addresses:
            - fc01:0:0:1::1/64
            nameservers: {}
    version: 2
~~~

# 構築手順
## DNS64の設定

DNS64サーバにはunboundを使用する。

~~~
sudo apt install unbound
~~~

設定ファイルを`/etc/unbound/unbound.conf.d/dns64.conf`に作成。

~~~:/etc/unbound/unbound.conf.d/dns64.conf
server:
  verbosity: 2
  pidfile: "/var/run/unbound.pid"
  use-syslog: yes
  module-config: "dns64 iterator"
  dns64-prefix: 64:ff9b::/96
  dns64-synthall: yes
  interface: ::0
  port: 53
  access-control: ::0/0 allow
 
forward-zone:
  name: "."
  forward-addr: 8.8.8.8
~~~

サービスを再起動。

~~~
sudo systemctl restart unbound
~~~

正常に起動できたことを確認(`sudo systemctl status unbound`など)したら、DNS64の動作を確認。

~~~
$ dig AAAA www.yahoo.co.jp

(中略)

;; ANSWER SECTION:
www.yahoo.co.jp.        19      IN      CNAME   edge12.g.yimg.jp.
~~~

~~~
$ dig AAAA www.yahoo.co.jp @localhost

(中略)

;; ANSWER SECTION:
www.yahoo.co.jp.        367     IN      CNAME   edge12.g.yimg.jp.
edge12.g.yimg.jp.       20      IN      AAAA    64:ff9b::b74f:d97c
~~~

unboundが起動しているlocalhostに問い合わせると、AAAAレコードを持っていない場合でも、
設定で指定されたprefixがついたIPv6アドレスが返ってくることを確認。

## RAの設定

IPv6のRoute Advertisementをするradvdをインストールする。
その前に、`/etc/sysctl.conf`にIPv4とIPv6パケットの転送を有効にするように設定。

~~~:/etc/sysctl.conf(追記)
net.ipv4.conf.all.forwarding = 1
net.ipv6.conf.all.forwarding = 1
~~~

設定を有効化。

~~~
sudo sysctl -p
~~~

radvdをインストール。

~~~
sudo apt install radvd
~~~

上記のコマンドでインストールすると、インストール後そのままradvdを起動しようとするのだが、`/etc/radvd.conf`が無いから起動できないよ、と言われるので、設定ファイルの`/etc/radvd.conf`を下記のように作成、設定する。

~~~:/etc/radvd.conf
interface eth1 {
  AdvSendAdvert on;
  MinRtrAdvInterval 3;
  MaxRtrAdvInterval 10;
  AdvOtherConfigFlag on;

  prefix fc01:0:0:1::/64 {
    AdvOnLink on;
    AdvAutonomous on;
    AdvRouterAddr on;
  };
};
~~~

radvdを再起動し、エラーが出なければOK。

~~~
sudo systemctl enable radvd
sudo systemctl restart radvd
~~~

## DHCPv6の設定

DHCPサーバには、もう古いとは言われているものの情報が多いのでisc-dhcp-serverを使う。
バージョンによって設定ファイルの書き方が変わっているので要注意。

~~~
sudo apt install isc-dhcp-server
~~~

設定ファイル(`/etc/default/isc-dhcp-server`と`/etc/dhcp/dhcpd6.conf`)を次のようにコメントアウト/設定。

~~~:/etc/default/isc-dhcp-server
DHCPDv6_CONF=/etc/dhcp/dhcpd6.conf
DHCPDv6_PID=/var/run/dhcpd6.pid
INTERFACESv6="eth1"
~~~

~~~:/etc/dhcp/dhcpd6.conf(追記)
subnet6 fc01:0:0:1::/64
{
  option dhcp6.name-servers fc01:0:0:1::1;
}
~~~

そして、今回最大のハマリポイントだったのだが、DHCPv6サーバのサービス名が、
`isc-dhcp-server`から**`isc-dhcp-server6`**に変わっていた。

下記のように、DHCPv4は無効化し、DHCPv6を有効化、起動する。

~~~
sudo systemctl disable isc-dhcp-server
sudo systemctl enable isc-dhcp-server6
sudo systemctl start isc-dhcp-server6
~~~

## 別のマシン(Host B)から確認

ここまで設定した状態で、一度IPv6オンリーのネットワークに繋いだホストBで下記を確認する。

* `ip a`で割り当てられたIPv6アドレスを確認
* `systemd-resolve --status`でDHCPv6でDNSサーバの情報が降ってきていることを確認
* `dig`でDNS64の名前解決ができることを確認

~~~
$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether fa:75:52:f4:58:e2 brd ff:ff:ff:ff:ff:ff
    inet6 fc01::1:f875:52ff:fef4:58e2/64 scope global dynamic mngtmpaddr noprefixroute 
       valid_lft 86394sec preferred_lft 14394sec
    inet6 fe80::f875:52ff:fef4:58e2/64 scope link 
       valid_lft forever preferred_lft forever
~~~

~~~
$ systemd-resolve --status
(中略)
    DNS Servers: fc:0:0:1::1
~~~

~~~
$ dig AAAA www.yahoo.co.jp

(中略)

;; ANSWER SECTION:
www.yahoo.co.jp.        415     IN      CNAME   edge12.g.yimg.jp.
edge12.g.yimg.jp.       39      IN      AAAA    64:ff9b::b74f:d97c
~~~

## NAT64(Jool)のビルドと設定

構築手順は[公式ページ](https://www.jool.mx/en/install.html)に準ずるが、
Jool 4.0から設定方法やコマンドが大幅に変わっているので注意されたし。

まずはビルドに必要なパッケージをインストール。

~~~
sudo apt install gcc make linux-headers-$(uname -r) libxtables-dev \
  dkms git autoconf tar pkg-config libnl-3-dev libnl-genl-3-dev
~~~

次にソースをダウンロードする。
Gitで最新版を落とすと`./configure`が文法エラーで止まったのでtarballで4.0.0をダウンロード。

~~~
wget https://github.com/NICMx/Jool/releases/download/v4.0.0/jool_4.0.0.tar.gz
tar -xzf jool_4.0.0.tar.gz
~~~

DKMSでインストール。

~~~
sudo dkms install jool-4.0.0/
~~~

次にuserspace clientsをビルド。

~~~
cd jool-4.0.0/
./configure
cd src/usr/
make
sudo make install
~~~

ビルドができたら、次のように設定、起動する。
以前のバージョンと全く異なるので要注意。

~~~
sudo modprobe jool

sudo jool instance add "example" --iptables  --pool6 64:ff9b::/96

sudo ip6tables -t mangle -A PREROUTING -d 64:ff9b::/96 -j JOOL --instance "example"
sudo iptables  -t mangle -A PREROUTING -d 192.168.10.217 -p tcp --dport 61001:65535 -j JOOL --instance "example"
sudo iptables  -t mangle -A PREROUTING -d 192.168.10.217 -p udp --dport 61001:65535 -j JOOL --instance "example"
sudo iptables  -t mangle -A PREROUTING -d 192.168.10.217 -p icmp -j JOOL --instance "example"
~~~

## NAT64の確認(Host Bから)

まずはIPv4ネットワーク側にPingが通ることを確認。

~~~
ping6 64:ff9b::192.168.10.253
~~~

あとはcurlで適当なウェブサイトを見てみて、ダウンロードできればOK。

~~~
curl www.yahoo.co.jp
~~~

Host Bに入れたのがサーバOSなのであまりアプリケーションがないのだが、
IPv6 onlyなネットワークに接続したホストから、IPv4ネットワークに接続する種々のアプリケーションが動作するはず。

## 自動起動の設定

再起動後に自動でJoolが起動するように設定する。

一旦、Joolを停止。

~~~
sudo ip6tables -t mangle -F
sudo iptables  -t mangle -F
sudo jool instance remove "example"
sudo modprobe -r jool
~~~

SystemdのUnitとその設定ファイルを作成する。

~~~:/etc/systemd/system/jool.service
[Unit]
Description=Jool - NAT64
After=network.target

[Service]
Type=oneshot
RemainAfterExit=yes
EnvironmentFile=/etc/jool.conf
ExecStart=/bin/sh -ec '\
    /sbin/modprobe jool; \
    /usr/local/bin/jool instance add "${JOOL_INSTANCE_NAME}" --iptables  --pool6 ${JOOL_IPV6_POOL}; \
    /sbin/ip6tables -t mangle -A PREROUTING -d ${JOOL_IPV6_POOL} -j JOOL --instance "${JOOL_INSTANCE_NAME}"; \
    /sbin/iptables  -t mangle -A PREROUTING -d ${JOOL_IPV4_ADDRESS} -p tcp --dport ${JOOL_NAPT_START}:${JOOL_NAPT_END} -j JOOL --instance "${JOOL_INSTANCE_NAME}"; \
    /sbin/iptables  -t mangle -A PREROUTING -d ${JOOL_IPV4_ADDRESS} -p udp --dport ${JOOL_NAPT_START}:${JOOL_NAPT_END} -j JOOL --instance "${JOOL_INSTANCE_NAME}"; \
    /sbin/iptables  -t mangle -A PREROUTING -d ${JOOL_IPV4_ADDRESS} -p icmp -j JOOL --instance "${JOOL_INSTANCE_NAME}" '


ExecStop=/sbin/ip6tables -t mangle -F
ExecStop=/sbin/iptables  -t mangle -F
ExecStop=/usr/local/bin/jool instance remove "${JOOL_INSTANCE_NAME}"
ExecStop=/sbin/modprobe -r jool

[Install]
WantedBy=multi-user.target
~~~

~~~:/etc/jool.conf
JOOL_IPV4_ADDRESS="192.168.10.217"
JOOL_IPV6_POOL="64:ff9b::/96"
JOOL_INSTANCE_NAME="example"
JOOL_NAPT_START="61001"
JOOL_NAPT_END="65535"
~~~

Unitファイルの変更を反映させ、サービスとして起動してみる。

~~~
sudo systemctl daemon-reload
sudo systemctl enable jool
sudo systemctl start jool
~~~

上手くいったら、再起動してJoolが起動できていればOK

~~~
sudo reboot
~~~

# その他(Joolの状態確認)

## インスタンス一覧

~~~
$ sudo jool instance display
+--------------------+-----------------+-----------+
|          Namespace |            Name | Framework |
+--------------------+-----------------+-----------+
| 0xffffffffacde4640 |         example |  iptables |
+--------------------+-----------------+-----------+
~~~

## インスタンスの設定

~~~
$ sudo jool -i example global display
  manually-enabled: true
  pool6: 64:ff9b::/96
  zeroize-traffic-class: false
  override-tos: false
  tos: 0
  mtu-plateaus: 65535,32000,17914,8166,4352,2002,1492,1006,508,296,68
  address-dependent-filtering: false
  drop-icmpv6-info: false
  drop-externally-initiated-tcp: false
  udp-timeout: 00:05:00 (HH:MM:SS)
  icmp-timeout: 00:01:00 (HH:MM:SS)
  tcp-est-timeout: 02:00:00 (HH:MM:SS)
  tcp-trans-timeout: 00:04:00 (HH:MM:SS)
  maximum-simultaneous-opens: 10
  source-icmpv6-errors-better: true
  f-args: 11 (0b1011): SrcAddr:1 SrcPort:0 DstAddr:1 DstPort:1
  handle-rst-during-fin-rcv: false
  logging-bib: false
  logging-session: false
  ss-enabled: false
  ss-flush-asap: true
  ss-flush-deadline: 2000
  ss-capacity: 512
  ss-max-payload: 1452
~~~

## セッション一覧

~~~
$ sudo jool -i example session display
---------------------------------
(V4_FIN_V6_FIN_RCV) Expires in 00:03:20.268
Remote: steelix.canonical.com#http      fc01::1:f875:52ff:fef4:58e2#50844
Local: 192.168.10.217#61190     64:ff9b::5bbd:5898#80
---------------------------------
(V6_INIT) Expires in 00:03:07.784
Remote: keeton.canonical.com#http       fc01::1:f875:52ff:fef4:58e2#34530
Local: 192.168.10.217#61940     64:ff9b::5bbd:58a1#80
---------------------------------
~~~

# 参考サイト

* [IPv6 only ネットワーク を作ってみる](https://blog.techlab-xe.net/archives/5269)
* [iOS向けIPv6なNAT64/DNS64の試験ネットワークをRaspberry Pi 3で作る](https://pslabo.hatenablog.com/entry/2016/07/30/iOS%E5%90%91%E3%81%91IPv6%E3%81%AANAT64/DNS64%E3%81%AE%E8%A9%A6%E9%A8%93%E3%83%8D%E3%83%83%E3%83%88%E3%83%AF%E3%83%BC%E3%82%AF%E3%82%92Raspberry_Pi_3%E3%81%A7%E4%BD%9C%E3%82%8B)
* [【NAT64】[Jool] Linuxだけで実装するNAT64(設計・構築編)](https://qiita.com/onifinger/items/b3262e0ce9febb776d41)
* [Fedora 23でWi-Fi APをたててNAT64してみた](https://diary.sshida.com/20151114-7-Fedora%2023%E3%81%A7Wi-Fi%20AP%E3%82%92%E3%81%9F%E3%81%A6%E3%81%A6NAT64%E3%81%97%E3%81%A6%E3%81%BF)

※ いずれもJool 4.0以前の情報なので要注意