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ネットワークに段階的に導入可能です。
本記事は、LISPをOSSで動かしてみたいというモチベーションで執筆しているため、基本的な動作説明は省略し、動作確認のみを説明します。LISPの基本的な動作については、 ゆるふわねっとわーく - LISP 概要/基本動作やSig9 Memo v4.0 - はじめての LISP (Location/Identifier Separation Protocol)が非常にわかりやすく、参考になります。日本語でLISPについてここまで丁寧に説明してくれている記事は少なく、とても貴重です。
用語説明
まずはじめに、以降で利用するLISPに関連する用語を簡単に説明します。
【参考: RFC 9300 - The Locator/ID Separation Protocol (LISP)】
- Routing Locator (RLOC): ルーティングのためのネットワーク識別子で、IPアドレスで表されます。カプセル化や脱カプセル化を行うLISPルータが持つIPアドレスです。
- Endpoint ID (EID): ノードを一意に識別するためのエンドポイント識別子で、これもIPアドレスで表されます。従来通り、端末やサーバに割り当てられるIPアドレスです。
- EID-Prefix: EIDのプレフィックスのことです。
- Ingress Tunnel Router (ITR): カプセル化を行うLISPルータです。LISPサイト内から受信したパケットの宛先IPアドレス(EID)からRLOCのルックアップを行い、カプセル化します。このルックアップにはMap-Server/Map-Resolverを利用します。
- Egress Tunnel Router (ETR): 脱カプセル化を行うLISPルータです。Outer Headerの宛先IPアドレスと自身の持つRLOCが一致する場合に脱カプセル化し、パケットをLISPサイト内へ転送します。また、ETRは、自身が持つRLOCと接続されているLISPサイトのEID-Prefixとのマッピング情報をMap-Serverに登録します。
- xTR: ITR機能とETR機能の両方を実装するルーターです。
【参考: 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、Android、iOS、VPPなど様々なプラットフォームやデータプレーンに対応しているようなので開発当時の本気度が伺えます。また、IPv6もサポートしています。非常に貴重なLISPのOSS実装ということで、今回は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を返すことを要求します。

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

このときの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を送信します。

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

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

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

おわりに
本記事では、LISPのOSS実装である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