CUBE SUGAR CONTAINER

技術系のこと書きます。

Linux の Network Namespace と radvd / dnsmasq で IPv6 SLAAC (+RDNSS) を試す

今回は、Linux の Network Namespace と radvd / dnsmasq を使って IPv6 の SLAAC を試してみる。 IPv6 では、アドレスの自動設定にいくつかのやり方がある。 SLAAC というのは、そのひとつで RFC 4862 で定義されている IPv6 Stateless Address Autoconfiguration のことを指す。 SLAAC では ICMPv6 NDP (Neighbor Discovery Protocol) の RA (Router Advertisement) というメッセージでアドレスとデフォルトルートを設定する。 その上で、RFC 8415 で定義されている Stateless DHCPv6 というプロトコルを使って DNS や NTP サーバを設定する。 なお、現在では RFC 8106 で定義されている RDNSS というオプションを使うことで、RA 単独でも DNS サーバを設定することができる。

使った環境は次のとおり。

$ cat /etc/lsb-release 
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=18.04
DISTRIB_CODENAME=bionic
DISTRIB_DESCRIPTION="Ubuntu 18.04.4 LTS"
$ uname -r
4.15.0-76-generic
$ dpkg -l | egrep "(radvd|dnsmasq|isc-dhcp-client)"
ii  dnsmasq                         2.79-1                              all          Small caching DNS proxy and DHCP/TFTP server
ii  dnsmasq-base                    2.79-1                              amd64        Small caching DNS proxy and DHCP/TFTP server
ii  isc-dhcp-client                 4.3.5-3ubuntu7.1                    amd64        DHCP client for automatically obtaining an IP address
ii  radvd                           1:2.16-3                            amd64        Router Advertisement Daemon

もくじ

作るネットワーク

用意するネットワークの物理的な構成は次のとおり。 白抜きになっている箱が Network Namespace を表している。 また、端点をもった線は veth インターフェイスを表している。

f:id:momijiame:20200208172938p:plain
ネットワークの物理的な構成

ネットワークの論理的な構成は次のとおり。

f:id:momijiame:20200208172451p:plain
ネットワークの論理的な構成

実験では host のアドレスやデフォルトルートを設定することになる。

下準備

下準備として、必要なパッケージをインストールしておく。

$ sudo apt-get -y install radvd dnsmasq isc-dhcp-client tcpdump

ネットワークを作る

まずは Network Namespace を作る。

$ sudo ip netns add host
$ sudo ip netns add router

そして、Network Namespace 同士をつなぐ veth インターフェイスを作る。

$ sudo ip link add ht-veth0 type veth peer name gw-veth0

作ったインターフェイスを Network Namespace に所属させる。

$ sudo ip link set ht-veth0 netns host
$ sudo ip link set gw-veth0 netns router

デフォルトでは EUI-64 を使ってアドレスの下位 64 ビットが生成されるため、わかりやすいように MAC アドレスを変更しておく。

$ sudo ip netns exec host ip link set dev ht-veth0 address 00:00:5E:00:53:01
$ sudo ip netns exec router ip link set dev gw-veth0 address 00:00:5E:00:53:02

veth インターフェイスの状態を UP に設定する。

$ sudo ip netns exec host ip link set ht-veth0 up
$ sudo ip netns exec router ip link set gw-veth0 up

router の方にリンクローカルアドレス (fe80::1 と、グローバルアドレスを模したドキュメンテーションアドレス (2001:db8::1) を付与しておく。

$ sudo ip netns exec router ip address add fe80::1/64 dev gw-veth0
$ sudo ip netns exec router ip address add 2001:db8::1/64 dev gw-veth0

次のようにアドレスが付与された。

$ sudo ip netns exec router ip address show gw-veth0
16: gw-veth0@if17: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 00:00:5e:00:53:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 2001:db8::1/64 scope global 
       valid_lft forever preferred_lft forever
    inet6 fe80::1/64 scope link 
       valid_lft forever preferred_lft forever
    inet6 fe80::200:5eff:fe00:5302/64 scope link 
       valid_lft forever preferred_lft forever

あとは router の方で IPv6 のルーティングを有効にしておく。 ちなみに、このカーネルパラメータのフラグを立てておかないと radvd が起動しない。

$ sudo ip netns exec router sysctl -w net.ipv6.conf.all.forwarding=1

radvd

はじめに、radvd から試してみる。 このプログラムは ICMPv6 NDP の RA を送ることができる。

はじめに、radvd の設定ファイルを用意する。 次の設定で、プレフィックスとして 2001:db8::/64 を広告しつつ、デフォルトルートを 2001:db8::1 に向けることができる。

cat << 'EOF' > radvd.conf
interface gw-veth0 {

    # 定期的にルータ広告を送る
    AdvSendAdvert on;

    # SLAAC でアドレスの自動生成に使うプレフィックスを広告する
    prefix 2001:db8::/64 { };

};
EOF

通信を観察するために tcpdump をしかけておく。

$ sudo ip netns exec host tcpdump -tnlvv -i ht-veth0 ip6
tcpdump: listening on ht-veth0, link-type EN10MB (Ethernet), capture size 262144 bytes

準備ができたら、用意した設定ファイルを使って radvd を起動する。

$ sudo ip netns exec router radvd -C radvd.con

すると、次のように prefix オプションを含む RA メッセージが出る。

$ sudo ip netns exec host tcpdump -tnlvv -i ht-veth0 ip6
tcpdump: listening on ht-veth0, link-type EN10MB (Ethernet), capture size 262144 bytes
IP6 (flowlabel 0xa72b0, hlim 255, next-header ICMPv6 (58) payload length: 56) fe80::1 > ff02::1: [icmp6 sum ok] ICMP6, router advertisement, length 56
    hop limit 64, Flags [none], pref medium, router lifetime 1800s, reachable time 0ms, retrans timer 0ms
      prefix info option (3), length 32 (4): 2001:db8::/64, Flags [onlink, auto], valid time 86400s, pref. time 14400s
        0x0000:  40c0 0001 5180 0000 3840 0000 0000 2001
        0x0010:  0db8 0000 0000 0000 0000 0000 0000
      source link-address option (1), length 8 (1): 00:00:5e:00:53:02
        0x0000:  0000 5e00 5302

host のインターフェイスは、RA を受信して prefix オプションを使ってアドレスを自動設定する。

$ sudo ip netns exec host ip address show dynamic ht-veth0
11: ht-veth0@if10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 00:00:5e:00:53:01 brd ff:ff:ff:ff:ff:ff link-netnsid 1
    inet6 2001:db8::200:5eff:fe00:5301/64 scope global dynamic mngtmpaddr 
       valid_lft 86379sec preferred_lft 14379sec

また、同時にルータ広告を送ってきたリンクローカルアドレスにデフォルトルートを設定する。

$ sudo ip netns exec host ip -6 route show
2001:db8::/64 dev ht-veth0 proto kernel metric 256 expires 86388sec pref medium
fe80::/64 dev ht-veth0 proto kernel metric 256 pref medium
default via fe80::1 dev ht-veth0 proto ra metric 1024 expires 1788sec hoplimit 64 pref medium

RDNSS の設定を追加してみる

RA の基本的な動作が確認できたので、つづいては RDNSS の設定を追加してパケットを観察してみる。

radvd の設定ファイルに RDNSS の設定を追加する。 配布する DNS サーバのアドレスは 2001:db8::dead:beef に指定した。

$ cat << 'EOF' > radvd.conf
interface gw-veth0 {

    # 定期的にルータ広告を送る
    AdvSendAdvert on;

    # SLAAC でアドレスの自動生成に使うプレフィックスを広告する
    prefix 2001:db8::/64 { };

    # RDNSS (RFC 8106) で DNS サーバを広告する
    RDNSS 2001:db8::dead:beef { };
};
EOF

radvd のプロセスに SIGHUP を送って設定ファイルを読み直させる。

$ sudo kill -HUP $(cat /var/run/radvd.pid)

すると、次のとおり RDNSS オプションを含む RA が観察できた。

$ sudo ip netns exec host tcpdump -tnlvv -i ht-veth0 ip6
tcpdump: listening on ht-veth0, link-type EN10MB (Ethernet), capture size 262144 bytes

...(snip)...

IP6 (flowlabel 0xa72b0, hlim 255, next-header ICMPv6 (58) payload length: 80) fe80::1 > ff02::1: [icmp6 sum ok] ICMP6, router advertisement, length 80
    hop limit 64, Flags [none], pref medium, router lifetime 1800s, reachable time 0ms, retrans timer 0ms
      prefix info option (3), length 32 (4): 2001:db8::/64, Flags [onlink, auto], valid time 86400s, pref. time 14400s
        0x0000:  40c0 0001 5180 0000 3840 0000 0000 2001
        0x0010:  0db8 0000 0000 0000 0000 0000 0000
      rdnss option (25), length 24 (3):  lifetime 600s, addr: 2001:db8::dead:beef
        0x0000:  0000 0000 0258 2001 0db8 0000 0000 0000
        0x0010:  0000 dead beef
      source link-address option (1), length 8 (1): 00:00:5e:00:53:02
        0x0000:  0000 5e00 5302

dnsmasq (RA + Stateless DHCPv6)

つづいては dnsmasq を使って RA + Stateless DHCPv6 のパターンを検証してみる。

どうやら dnsmasq には RA を送る機能もあるようなので、いったん radvd のプロセスは kill しておく。

$ sudo kill $(cat /var/run/radvd.pid)

あらためて tcpdump をしかけておく。

$ sudo ip netns exec host tcpdump -tnlvv -i ht-veth0 ip6

そして、dnsmasq を起動する。 --enable-ra オプションが RA を送る指定になっている。

$ sudo ip netns exec router dnsmasq \
  --enable-ra \
  --dhcp-range=::,constructor:gw-veth0,ra-stateless \
  --dhcp-option=option6:dns-server,[2001:db8::dead:beef] \
  --dhcp-option=option6:ntp-server,[2001:db8::dead:beef] \
  --no-daemon

準備ができたら isc-dhcp の dhclient を起動する。 -6 オプションと -S オプションを組み合わせることで IPv6 SLAAC のモードになる。

$ sudo ip netns exec host dhclient -6 -S ht-veth0

tcpdump のターミナルを確認すると、次のとおり RA と Stateless DHCPv6 のパケットがやり取りされていることがわかる。 なお、RA にはデフォルトで RDNSS オプションが付与されるらしい。 まあ、たしかに Stateless DHCPv6 と同じ DNS サーバのアドレスを配布するなら、オプションがあっても副作用はとくにないのかな?

$ sudo ip netns exec host tcpdump -tnlvv -i ht-veth0 ip6
tcpdump: listening on ht-veth0, link-type EN10MB (Ethernet), capture size 262144 bytes

... (snip) ...

IP6 (class 0xc0, flowlabel 0xa72b0, hlim 255, next-header ICMPv6 (58) payload length: 88) fe80::1 > ff02::1: [icmp6 sum ok] ICMP6, router advertisement, length 88
    hop limit 64, Flags [other stateful], pref medium, router lifetime 1800s, reachable time 0ms, retrans timer 0ms
      prefix info option (3), length 32 (4): 2001:db8::/64, Flags [onlink, auto], valid time 3600s, pref. time 3600s
        0x0000:  40c0 0000 0e10 0000 0e10 0000 0000 2001
        0x0010:  0db8 0000 0000 0000 0000 0000 0000
      mtu option (5), length 8 (1):  1500
        0x0000:  0000 0000 05dc
      source link-address option (1), length 8 (1): 00:00:5e:00:53:02
        0x0000:  0000 5e00 5302
      rdnss option (25), length 24 (3):  lifetime 3600s, addr: 2001:db8::dead:beaf
        0x0000:  0000 0000 0e10 2001 0db8 0000 0000 0000
        0x0010:  0000 dead beaf

IP6 (flowlabel 0x5f40a, hlim 1, next-header UDP (17) payload length: 48) fe80::200:5eff:fe00:5301.546 > ff02::1:2.547: [bad udp cksum 0xafc9 -> 0x2ffe!] dhcp6 inf-req (xid=29221d (client-ID hwaddr/time type 1 time 634459916 00005e005301) (option-request DNS-server DNS-search-list Client-FQDN SNTP-servers) (elapsed-time 0))

IP6 (class 0xc0, flowlabel 0x48785, hlim 64, next-header UDP (17) payload length: 76) fe80::1.547 > fe80::200:5eff:fe00:5301.546: [bad udp cksum 0xaf61 -> 0x705b!] dhcp6 reply (xid=29221d (client-ID hwaddr/time type 1 time 634459916 00005e005301) (server-ID hwaddr/time type 1 time 634459172 00005e005302) (DNS-server 2001:db8::dead:beaf) (lifetime 3600))

めでたしめでたし。

参考文献

tools.ietf.org

tools.ietf.org

tools.ietf.org

linux.die.net

linux.die.net

linux.die.net