LISP(Locator/ID Separation Protocol)を動かしたい!

本記事の内容について、間違いや解釈違いを見つけた場合はXアカウント@yy_kzmでお知らせいただけると助かります。

LISPとは

LISP(Locator/ID Separation Protocol)とは、名前の通りルーティングのためのネットワーク識別子(Locator)とノードを一意に識別するためのエンドポイント識別子(ID)を分離するプロトコルです。現在のインターネットアーキテクチャでは、これら2つ情報は1つのIPアドレスとして表現されます。LISPでは2つのIPアドレスを利用して、LocatorとIDを別々の論理空間として定義し、それらのマッピングカプセル化技術を使うことで通信を実現します。LISPは、ホストのプロトコルスタックに変更を加える必要はなく、既存のIPネットワークに段階的に導入可能です。

本記事は、LISPOSSで動かしてみたいというモチベーションで執筆しているため、基本的な動作説明は省略し、動作確認のみを説明します。LISPの基本的な動作については、 ゆるふわねっとわーく - LISP 概要/基本動作Sig9 Memo v4.0 - はじめての LISP (Location/Identifier Separation Protocol)が非常にわかりやすく、参考になります。日本語でLISPについてここまで丁寧に説明してくれている記事は少なく、とても貴重です。

用語説明

まずはじめに、以降で利用するLISPに関連する用語を簡単に説明します。

【参考: RFC 9300 - The Locator/ID Separation Protocol (LISP)

【参考: RFC 9301 - Locator/ID Separation Protocol (LISP) Control Plane

  • Map-Server (MS): ETRからマッピング情報を受け取り、データベースとして保存するサーバです。
  • Map-Resolver (MR): ITRからEncapsulated Map-Requestを受け取り、宛先EIDがMap-Serverの管理対象に該当するかを確認するサーバです。該当しない場合は、Negative Map-Replyと呼ばれる空のRLOCをを含むメッセージをITRへ返します。該当する場合は、Map-Serverのデータベース参照し、適切なRLOCを発見します。Map-ServerとMap-Resolverは単一のサーバで実装することができ、多くの場合はそのように配置されます。
  • Map-Registerメッセージ: ETRがMap-Serverに自身のRLOCと接続されているLISPサイトのEID-Prefixとのマッピング情報を登録するためのメッセージです。
  • (Encapsulated) Map-Requestメッセージ: EIDにマッピングされているRLOCを解決(ルックアップ)するための要求メッセージです。(ITR→MS/MR→ETR)
  • Map-Replyメッセージ: 解決されたRLOCを知らせるための応答メッセージです。(ETR→ITR)
  • Map-Notifyメッセージ: Map-ServerからETRへマッピング情報が正常に登録されたことを通知するメッセージです。ETRは、Map-Registerメッセージの「Want-Map-Notify」フラグ(Mビット)を設定することにより、Map-Notifyを返すことを要求します。

Open Overlay Router

Open Overlay Router(OOR: double-O R)は、C言語で実装されたプログラマブルなオーバーレイネットワークを構築するためのオープンソースルータです。OORはRFC6830(Experimental)で標準化されたLISPを実装しています。ただし、2019年/2020年辺りから更新がないので現在アクティブなプロジェクトなのかどうかはよくわかりません。OpenWrt、AndroidiOS、VPPなど様々なプラットフォームやデータプレーンに対応しているようなので開発当時の本気度が伺えます。また、IPv6もサポートしています。非常に貴重なLISPOSS実装ということで、今回はOORを使ってLISPの基本的な動作を確認したいと思います。

動作確認

まずは、OORのソースコードをcloneします。

$ git clone https://github.com/OpenOverlayRouter/oor.git

OORはDockerをサポートしており、ハンズオンラボが用意されています。docker-compose-2-xtr-1ms.ymlを立ち上げると以下のようなネットワークが構築されます。

$ cd oor/Docker/compose-examples/2xtrs-1msmr
$ ls
docker-compose-2-xtr-1ms.yml  oor.msmr.conf  oor.xtr1.conf  oor.xtr2.conf  scenario.sh
$ sudo ./scenario.sh UP

▶︎ oor.msmr.conf

debug                  = 3
map-request-retries    = 2
log-file               = /var/log/oor.log
ipv6-scope             = SITE
operating-mode         = MS
control-iface          = eth0

# xTR1
lisp-site {
    eid-prefix            = 192.168.1.0/24
    key-type              = 1
    key                   = password
    iid                   = 0
    accept-more-specifics = true
}

lisp-site {
    eid-prefix            = fd00:1::/64
    key-type              = 1
    key                   = password
    iid                   = 0
    accept-more-specifics = true
}

# xTR2
lisp-site {
    eid-prefix            = 192.168.2.0/24
    key-type              = 1
    key                   = password
    iid                   = 0
    accept-more-specifics = true
}

lisp-site {
    eid-prefix            = fd00:2::/64
    key-type              = 1
    key                   = password
    iid                   = 0
    accept-more-specifics = true
}

▶︎ oor.xtr1.conf

debug                  = 1
map-request-retries    = 2
log-file               = /var/log/oor.log
ipv6-scope             = SITE
operating-mode         = xTR
encapsulation          = LISP
nat_traversal_support  = off
rloc-probing {
    rloc-probe-interval = 0
}
map-resolver = {
    10.0.0.2
}
map-server {
    address        = 10.0.0.2
    key-type       = 1
    key            = password
    proxy-reply    = off
}
database-mapping {
    eid-prefix     = 192.168.1.0/24
    iid            = 0
    ttl            = 10
    rloc-address {
        address    = 10.0.0.3
        priority   = 1
        weight     = 100
    }
}
database-mapping {
    eid-prefix     = fd00:1::/64
    iid            = 0
    ttl            = 10
    rloc-address {
        address    = 10.0.0.3
        priority   = 1
        weight     = 100
    }
}

▶︎ oor.xtr2.conf

debug                  = 1
map-request-retries    = 2
log-file               = /var/log/oor.log
ipv6-scope             = SITE
operating-mode         = xTR
encapsulation          = LISP
nat_traversal_support  = off
rloc-probing {
    rloc-probe-interval = 0
}
map-resolver = {
    10.0.0.2
}
map-server {
    address        = 10.0.0.2
    key-type       = 1
    key            = password
    proxy-reply    = off
}
database-mapping {
    eid-prefix     = 192.168.2.0/24
    iid            = 0
    ttl            = 10
    rloc-address {
        address    = 10.0.0.4
        priority   = 1
        weight     = 10
    }
}
database-mapping {
    eid-prefix     = fd00:2::/64
    iid            = 0
    ttl            = 10
    rloc-address {
        address    = 10.0.0.4
        priority   = 1
        weight     = 10
    }
}

立ち上げ完了後、client1からclient2のEIDにpingを打ってみます。すると、最初の数回は失敗しますが、その後は疎通が確認できます。

$ docker exec -it client1 ping 192.168.2.3 
PING 192.168.2.3 (192.168.2.3): 56 data bytes
64 bytes from 192.168.2.3: seq=2 ttl=61 time=0.889 ms
64 bytes from 192.168.2.3: seq=3 ttl=61 time=0.815 ms
64 bytes from 192.168.2.3: seq=4 ttl=61 time=0.667 ms
64 bytes from 192.168.2.3: seq=5 ttl=61 time=0.804 ms
^C
--- 192.168.2.3 ping statistics ---
6 packets transmitted, 4 packets received, 33% packet loss
round-trip min/avg/max = 0.667/0.793/0.889 ms

このとき、LISPがどのような通信をしているのかを確認します。

xtr2からMap-Server(msmr)へMap-Registerを送信し、マッピング情報を登録します(xtr1も同様)。このとき、「Want-Map-Notify」フラグ(Mビット)を設定することにより、Map-Notifyを返すことを要求します。

xtr2からmsmrへのMap-Register

マッピング情報の登録処理が完了すると、Map-Server(msmr)からMap-Notifyメッセージが返されます。

msmrからxtr2へのMap-Nortify

このときのMap-Server(msmr)では次のようなログが確認できます。

$ docker exec -it oor-msmr oor
...
[2024/6/18 16:42:48] DEBUG: Received Map-Register -> flags:pirM record-count: 1 nonce 6bf9f9775cdff663, IP: 10.0.0.4 -> 10.0.0.2, UDP: 4342 -> 4342
[2024/6/18 16:42:48] DEBUG:   Mapping-record -> ttl: 10 loc-count: 1 action: no-action auth: 1 map-version: 0 eid: 192.168.2.0/24
[2024/6/18 16:42:48] DEBUG:     Locator-record -> flags: L=1,p=0,R=1, p/w: 1/10 255/0, addr: 10.0.0.4
...
[2024/6/18 16:42:48] DEBUG-2: The map cache entry of EID 192.168.2.0/24 will expire in 180 seconds.
[2024/6/18 16:42:48] DEBUG-3: **************** MS registered sites ******************
[2024/6/18 16:42:48] DEBUG-3: EID: 192.168.2.0/24, ttl: 10, loc-count: 1, action: no-action, auth: 1
  RLOC: 10.0.0.4, Up, L:0, R:1, 1/10, 255/0
[2024/6/18 16:42:48] DEBUG-3: *******************************************************
[2024/6/18 16:42:48] DEBUG: Map-Notify-> flags:ir, record-count: 1, nonce 6bf9f9775cdff663, IP: 10.0.0.2 -> 10.0.0.4, UDP: 4342 -> 4342
[2024/6/18 16:42:48] DEBUG: Sent control message IP: 10.0.0.2 -> 10.0.0.4 UDP: 4342 -> 4342
...

client1からclient2のEID(192.168.2.3)へpingを実行します。

このパケットを受け取ったxtr1は、宛先EIDに対応するRLOCをルックアップします。自身が持つEID-to-RLOCキャッシュに該当するエントリーがなければMap-Resolver(msmr)へMap-Requestを送信します。

xtr1からmsmrへのMap-Request

Map-Requestを受信したMap-Resolver(msmr)は、宛先EIDを確認し、Map-Serverへ転送します。今回はこれらは共存しているのでこのフェーズは図示していません。Map-Serverはデータベースを参照し、EIDに紐づけられたRLOCへMap-Requestを転送します。

msmrからxtr2へのMap-Request

Map-Requestを受信したxtr2は、xtr1へEID-PrefixとRLOCを含むMap-Replyを返します。

なぜ、Map-Serverが直接ITR(xtr1)にMap-Replyを返さないのだろうと思ったりしましたが、おそらくLISP-DDTと呼ばれるようなEID-Prefixを集約してツリー構造で管理するケースが想定されており、この辺りの構成が関係していそうです。

xtr2からxtr1へのMap-Reply

client1からclient2のEID(192.168.2.3)へping疎通が確認できます。

client1からclient2へのICMP Echo Request

おわりに

本記事では、LISPOSS実装であるOORを使ってLISPの基本動作を確認しました。LISPは関連RFCが多く、複雑なものだと思い込んでいましたが、基本動作はとてもシンプルなものだとわかりました。Locator/ID分離の考え方はLISP以外のプロトコルでも採用されていることがあり、この概念に対する理解が進んだのでよかったです。

デバッグ

動作確認にあたって、詰まった点があるので紹介します。(完全には解決していない..) xtr1で、msmrやxtr2のRLOCと通信しようとした際、ICMP Redirectを頻繁に確認しました。xtr1のルーティングテーブルとルーティングポリシーを見てみると以下のようになっていました。

root@6bd6d3d52bf9:/oor# ip r
default via 10.0.0.1 dev eth0
10.0.0.0/24 dev eth0  proto kernel  scope link  src 10.0.0.3
192.168.1.0/24 dev eth1  proto kernel  scope link  src 192.168.1.2
root@6bd6d3d52bf9:/oor# ip rule
0:      from all lookup local
99:     from all to 192.168.1.0/24 lookup main
100:    from 192.168.1.0/24 lookup 100
408:    from 10.0.0.3 lookup 152
32766:  from all lookup main
32767:  from all lookup default

408: from 10.0.0.3 lookup 152に着目して、テーブル152のルーティングテーブルを確認すると、以下のようになっており、msmrやxtr2のRLOCの通信が一旦10.0.0.1(0rlocのBridge)へ転送されてしまい、Redirectが発生していました。そもそも408: from 10.0.0.3 lookup 152のエントリーが何のためにあるのかわかりませんでした。(RLOCを複数保持する場合など?)

root@6bd6d3d52bf9:/oor# ip r s t 152
default via 10.0.0.1 dev eth0  proto static  metric 100