FD.io VPP 24.02でフレッツ(IPoE)ホームルーターを自作した時のメモ

# はじめに

自宅のフレッツ(flets)光回線用にホームルーターを[APU2](https://www.pcengines.ch/apu2.htm)を使って作成した時のメモです。マンション構内はVDSL接続なのでスピードが改善されるといったことはありませんが、自宅ネットワークのIPv6化が目的です。

これまでFD.io VPPについては、作業用のNAPTプライベートネットワークを作成するために利用してきました。

https://qiita.com/YasuhiroABE/items/0abe6ebe7a35006bcd65

またフレッツ光用のIPoEルーターをUbuntuで構築してきましたが、Ubuntu 22.04にアップグレードしてからIPv6のアドレスがDHCPv6-PD (Prefix Delegation)でうまく取得できなくなってしまい、ルーター以外のクライアントはIPv4のみを利用してきて、ほぼ全ての通信がIPv4 over IPv6なトンネルに依存していました。

https://qiita.com/YasuhiroABE/items/977096d4a1672378dd57

今回はこれらを組み合せて、VPPを使いDHCPv6-PDの問題を解決したので顛末を記録しておきます。

![20240520_fd.IO-vpp-ipoe-gateway.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/78296/556cdaba-d60b-c0af-74c2-2ec7d27444a7.png)

IPoE環境ではNTTからレンタルしているVDSL機器(HGW)がDHCPv6サーバーになっていて、機器情報を元にプロバイダーのグローバルIPv6アドレスを割り当ててくれるため自作ホームルーター単体のインターネット接続についてはDHCPv6クライアントを実行すれば問題ありません。

問題は宅内LANへのIPv6アドレスの割り当て方法が光でんわ契約の有無によって変化する点です。今回は光でんわ契約があるためDHCPv6-PDを利用しているわけですが、netplan + systemd-networkdの組み合せでうまく動作していないことが問題でした。

またIPv4接続はDS-LiteによるIPv4 over IPv6トンネルになっていてIPv4の固定IPは利用していません。

# 環境

基本的な環境は前述の過去記事に記載しているとおりです。今回の記事に関連する箇所と差分だけ記録しておきます。

* フレッツ光: 光でんわ契約有り
* Hardware: PC Engnes社製 APU2 (Memory: 4GB, SSD: 256GB)
* OS: Ubuntu 24.04 Server amd64版
* ISP: Interlink (インターリンク フレッツ接続 ZOOT NATIVE DS-Lite IPv4固定IP無し)
* FD.io VPP: 24.02 (jammy版 公式パッケージ)

:::note
VPPのパッケージについては、現在はnoble(ubuntu 24.04)版のパッケージが配布されています。
:::

なおNTTからレンタルしているHGW(VDSLルーター)は``192.168.1.0/24``ネットワークを利用しています。

# 参考資料

前述の自分の記事の他に以下の文書を参考にさせて頂きました。

https://s3-docs.fd.io/vpp/24.02/

https://s3-docs.fd.io/vpp/24.02/usecases/home_gateway.html

https://hrntknr.net/p/vpp-v6plus/

最後に掲載した記事はDocker+固定IP用で、今回はIPv4固定IP無しの場合なので構成が若干異なります。試していませんが固定IPを利用している場合はこの記事を参考にしてipip0に固定IPv4アドレスを割り当ててNAPTすれば動作するでしょう。

# 基本的な設定ファイル

## Netplan

**/etc/netplan/** 以下の設定ファイルは全て削除しています。

## VPP

**/etc/vpp/** に2つのファイルを配置しています。

### /etc/vpp/startup.cfg

```text:/etc/vpp/startup.cfg
unix {
  nodaemon
  log /var/log/vpp/vpp.log
  full-coredump
  cli-listen /run/vpp/cli.sock

  startup-config /etc/vpp/local.cfg
}

api-trace {
  on
}

api-segment {
  gid vpp
}

socksvr {
  default
}
cpu {
        main-core 1
        workers 3
}

dpdk {
        dev default {
                num-rx-desc 512
                num-tx-desc 512
        }
        no-multi-seg
        no-tx-checksum-offload

        dev 0000:01:00.0 {
                name enp1s0
                num-rx-queues 2
                num-tx-queues 2
        }
        dev 0000:02:00.0 {
                name enp2s0
                num-rx-queues 2
                num-tx-queues 2
        }
        dev 0000:03:00.0 {
                name enp3s0
                num-rx-queues 2
                num-tx-queues 2
        }
}
plugins {
  plugin default { disable }
  plugin nat_plugin.so { enable }
  plugin dpdk_plugin.so { enable }
  plugin dhcp_plugin.so { enable }
  plugin ping_plugin.so { enable }
}
```

### /etc/vpp/local.cfg

先頭で定義している定数は、適宜変更してください。

TRANSIX_GWはフレッツ東西でアドレスが違い、将来的に変更になる可能性がある点に注意してください。

ENP1S0_IPV6_ADDRは実際に割り当てられるIPv6アドレスで置き換えてください。

TAP0_IPV6_ADDRとTAP0_IPV6_GW_ADDRは静的に使われていないIPv6アドレスを指定します。この時にネットワーク部分/56をENP1S0_IPV6_ADDRに割り当てられているIPv6アドレスからコピーして置き換えてください。末尾の::2や::1はそのままで良いはずです。"xx20"の部分はおそらく同じだろうと思いますがDHCPv6-PDで割り当てられるネットワーク部分なのでVDSL(OUI)機器側から指定されるアドレスに応じて変更しなければいけない点に注意してください。よく分からなければVDSL機器のWebコンソールからDHCPv6-PDで割り当てているアドレスを確認することができます。

固定IPv4付きプランの場合は試していませんが、参考資料に上げた記事を読む限りは"192.168.3.1/24"を割り当てている部分を固定IPに変更して、こちら側でNAPTするように設定を追加すれば動作するはずです。

```text:/etc/vpp/local.cfg
define TRANSIX_GW 2404:8e00::feed:101
define ENP1S0_IPV6_ADDR 2409:xxxx:xxxx:xx00:20d:xxxx:xxxx:7b1c
define TAP0_IPV6_ADDR 2409:xxxx:xxxx:xx20::2/64
define TAP0_IPV6_GW_ADDR 2409:xxxx:xxxx:xx20::1

comment { for ipv4 global flets network }                                                                       set dhcp client intfc enp1s0 hostname ipoegw
set int state enp1s0 up

comment { for home network ipv4 }
bvi create instance 0
set int l2 bridge bvi0 1 bvi
set int ip address bvi0 192.168.2.1/24
set int state bvi0 up

comment { Add more inside interfaces as needed ... }
set int l2 bridge enp2s0 1
set int state enp2s0 up

comment { Setup routing for 192.168.1.0/24 network }
ip table 1
ip route add table 1 192.168.1.0/24 via enp1s0

comment { Configure NAT }
nat44 plugin enable sessions 63000
nat44 add interface address enp1s0
set interface nat44 out enp1s0 output-feature
nat44 forwarding enable

comment { Setup ipv6 }
set int ip6 table enp1s0 0
ip6 nd address autoconfig enp1s0 default-route
dhcp6 client enp1s0

dhcp6 pd client enp1s0 prefix group hgw
set ip6 address bvi0 prefix group hgw ::1/64
ip6 nd address autoconfig bvi0 default-route

comment { Setup tap0 device for host access }
create tap host-if-name tap0 host-ip4-addr 192.168.2.2/24 host-ip4-gw 192.168.2.1 host-ip6-addr $(TAP0_IPV6_ADDR) host-ip6-gw $(TAP0_IPV6_GW_ADDR)
set int l2 bridge tap0 1
set int state tap0 up

ip route add ::/0 via enp1s0

comment { Setup ipip ipv4 over ipv6 access }
create ipip tunnel src $(ENP1S0_IPV6_ADDR) dst $(TRANSIX_GW)
set int ip address ipip0 192.168.3.1/24
set int state ipip0 up

ip route add 0.0.0.0/0 via ipip0
```

## dnsmasq

dnsmasqはネットワークを192.168.2.0/24に対応させて、OSからみえるデバイスがtap0となっている点を書き換えた他は以前のものをそのまま利用しています。

# 設定内容の確認

OSからはipコマンドで確認すると次のようになっています。

```bash:OSからみえるネットワーク設定
$ ip addr
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 noprefixroute
       valid_lft forever preferred_lft forever
5: tap0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UNKNOWN group default qlen 1000
    link/ether 02:fe:3e:xx:xx:1e brd ff:ff:ff:ff:ff:ff
    inet 192.168.2.2/24 scope global tap0
       valid_lft forever preferred_lft forever
    inet6 2409:xxxx:xxxx:xx20::2/64 scope global
       valid_lft forever preferred_lft forever
    inet6 fe80::fe:3eff:xxxx:xx1e/64 scope link
       valid_lft forever preferred_lft forever

$ ip route
default via 192.168.2.1 dev tap0 proto unspec
192.168.2.0/24 dev tap0 proto kernel scope link src 192.168.2.2

$ ip -6 route
2409:xxxx:xxxx:xx20::/64 dev tap0 proto kernel metric 256 pref medium
fe80::/64 dev tap0 proto kernel metric 256 pref medium
default via 2409:xxxx:xxxx:xx20::1 dev tap0 metric 1024 pref medium
```

vppctlからは次のようにみえています。

```bash:vppctlの出力
bvi0 (up):
  L2 bridge bd-id 1 idx 1 shg 0 bvi
  L3 192.168.2.1/24
  L3 2409:xxxx:xxxx:xx20:b2b0:ff:fe00:0/64
  L3 2409:xxxx:xxxx:xx20:20d:xxxx:xxxx:7b1c/64
enp1s0 (up):
  L3 192.168.1.6/24
  L3 2409:xxxx:xxxx:xx00:::dee0/64
enp2s0 (up):
  L2 bridge bd-id 1 idx 1 shg 0
enp3s0 (dn):
ipip0 (up):
  L3 192.168.3.1/24
local0 (dn):
tap0 (up):
  L2 bridge bd-id 1 idx 1 shg 0
```

## クライアント側の設定状況

dnsmasqからIPを割り当てられた宅内LANの端末(Linux 22.04.4, Wi-Fi接続)の設定は次のようになっています。

```bash:ip routeの出力
$ ip route
default via 192.168.2.2 dev wlp3s0 proto dhcp metric 600
192.168.2.0/24 dev wlp3s0 proto kernel scope link src 192.168.2.220 metric 600
192.168.2.1 via 192.168.2.2 dev wlp3s0 proto dhcp metric 600

$ ip -6 route
::1 dev lo proto kernel metric 256 pref medium
2409:xxxx:xxxx:xx20::/64 dev wlp3s0 proto ra metric 600 pref medium
fe80::/64 dev wlp3s0 proto kernel metric 1024 pref medium
default via fe80::fe:3eff:xxxx:xx1e dev wlp3s0 proto ra metric 600 pref medium
```

IPv6のリンクローカルアドレスで構築されているネットワークは本当に良く出来てるなと思います。

# 設定で苦労した点

他で使っていたNAPTゲートウェイ用の設定にipipの部分を追加しようとしたら見事にはまりました。

原因はIPv4ベースで構築した構成ファイルにIPv6の設定をマージしようとしたことにあって、参考資料に挙げたVPPの公式ドキュメントの構成に従って最初から書き直して設定を徐々に加えて、``systemctl restart vpp.service``を実行して結果を試しながら進めることで解決しました。

その他に特に気になったのは次のような点です。

## tap0にipv6アドレスが割り当てできなかった

IPv4のサンプルなどをみるとvppctlから``ip int ip address …``コマンドで割り当てることもできるようでしたが、Ipv6ではcreate tap行で設定しないとOS側から認識できませんでした。

## ipip0は動作しているのにtap0を経由して接続できない

``sudo vppctl ping www.ibm.com``は正常に動作していてtransixへのIPv4 over IPv6トンネルは問題なく構成できるのに、LANからtap0を経由してアクセスすることができませんでした。

試行錯誤した結果、適当なIPv4アドレスを割り当てました。IPv4固定アドレスは契約していないので、他で定義していないネットワークアドレス(192.168.3.0/24)のIPアドレスを使っています。

この時に既に設定済みのネットワーク内のアドレス、例えば**192.168.2.254/24**のようなアドレスを指定すると、他の**192.168.2.1/24**などの割り当てができなくなりました。

## 資料が少なすぎる

一応動いていはいますが、私の理解が不十分で間違った設定が行われている可能性もありますのでご了承ください。

残念ながら、ここを読めばバッチリ、というものはないので公式ガイドでソースコード+RFCまで確認しないと本来はいけないのかもしれません。

# さいごに

Ubuntuのnetplanやsystemd-networkdに問題があるとは思っていませんが、最終的にVPPを使ってIPv6がうまく扱えない課題を解決しました。

以前はNTTからレンタルしているVDSL機器と同じ192.168.1.0/24ネットワークを使っていましたが、新規に作るタイミングで192.168.2.0/24を宅内LAN用に作りました。

結果的にルーティングもすっきりして、VDSL機器の管理画面にもスムーズにアクセスできるようになり以前よりも快適になったと思います。

# 後日談

ここまでで動いているので問題ないのですが、その後の変更についてまとめていきます。

## VoIPによる電話機の移動とIP Masqueradeの停止

手元にあったGrandstream社のHT801を利用して、NTTからレンタルしているHGWに接続していた電話機をリビングに移動しました。

HT801の設定自体は下記のサイトを参考にさせて頂きました。

https://denwa.dev/GrandstreamHT802HGW.pug

Primary SIP ServerとOutbound Proxyには、``192.168.1.1``のHGWのアドレスを指定しています。

宅内のネットワークはこのタイミングでAPU2で192.168.2.0/24から192.168.1.0/24に出ていく時にIP Masqueradeしていた設定を止めて、APU2のHGW側ポート(enp1s0)に``192.168.1.6/24``を指定しています。

IPv6を使えば、192.168.1.0/24へのアクセス方法を変更する必要はなかったのですが、HGW側の設定をみている時にLAN側で静的ルーティングの設定項目を見付けたので利用することにしました。

HGWからは192.168.2.0/24ネットワークへのトラフィックを192.168.1.6をゲートウェイに指定しています。

```diff:/etc/vpp/local.cfgファイルの差分
--- a   2024-05-23 10:49:18.631221971 +0900
+++ b   2024-05-23 10:50:56.615260818 +0900
@@ -4,7 +4,8 @@
 define TAP0_IPV6_GW_ADDR 2409:xxxx:xxxx:xx20::1

 comment { for ipv4 global flets network }
-set dhcp client intfc enp1s0 hostname ipoegw
+set int ip address enp1s0 192.168.1.6/24
+ip route add 192.168.1.0/24 via enp1s0
 set int state enp1s0 up

 comment { for home network ipv4 }
@@ -17,17 +18,7 @@
 set int l2 bridge enp2s0 1
 set int state enp2s0 up

-comment { Setup routing for 192.168.1.0/24 network }
-ip table 1
-ip route add table 1 192.168.1.0/24 via enp1s0
-
-comment { Configure NAT }
-nat44 plugin enable sessions 63000
-nat44 add interface address enp1s0
-set interface nat44 out enp1s0 output-feature
-nat44 forwarding enable
```

HGW側の静的ルーティングに依存はしていますが、IP Masqueradeからフラットなネットワークになってすっきりしました。

## IPv6のセキュリティについて

HGW側にあるIPv6ファイアウォールを**高度**に設定することで、NGNとインターネット側からの接続を拒否するようになります。

標準だとNGNからの接続は許可するので、フレッツ光を利用している個人や小規模オフィスから接続される可能性はありそうです。

しばらく様子をみてHGWの設定で十分か、APU2側のVPPで何か設定を加えるか判断したいと思います。

以上