SR-MPLSをIS-ISで試してみた話


## はじめに
普段はSegment Routing over IPv6 (SRv6)を扱っていたのですが，SR-MPLSも勉強しないとということでFRRoutingとMinientを使ってSR-MPLS動かしてみた話です．多くの記事ではOSPFを使っているので，逆張りでIS-ISを使ってSR-MPLSを動かします．

## 構築するネットワーク
今回は以下のネットワークを構築します．
![sr_mpls_isis_12.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2661127/2bbeac13-d59f-ea15-13b6-53f12a733c97.png)

* ルータのループバックは10.0.0.0/24
* Segment Routing Global Block (SRGB) を16000~23999とし，NodeSIDとしてルータごとに100単位でindexを割り当て
* IGPはIS-ISを使用


## 環境設定
FRRoutingとMinientを使ったツールを用いて，ネットワークを構築します．


### FRRoutingのインストール
```
sudo apt install -y frr
```

### MPlSの設定
LinuxにMPLSのモジュールを読み込む必要があるので，以下のコマンドを実行します．
```
sudo apt install linux-modules-extra-`uname -r`
sudo modprobe mpls_router
sudo modprobe mpls_gso
sudo modprobe mpls_iptunnel
```

## Mininetのインストール
Mininetをインストールしていきます．
```
git clone https://github.com/mininet/mininet
cd mininet
git tag  # list available versions
git checkout -b mininet-2.3.0 2.3.0  # or whatever version you wish to install
cd ..
mininet/util/install.sh -a

sudo apt -y install openvswitch-switch
sudo service openvswitch-switch start
```

MinientでFRRoutingを使うには工夫が必要です．
今回は，自分が作成した[`ipnet_mininet`](https://github.com/shu1r0/ipnet_mininet)を使います．
このツールは，Mininetでルーティングプロトコルを動かせるように，拡張やヘルパーを追加したツールです．

https://github.com/shu1r0/ipnet_mininet

```
git clone https://github.com/shu1r0/ipnet_mininet.git
cd ipnet_mininet/
sudo pip install -r requirements.txt
sudo pip install .
```

## FRRoutingの設定
例としてR1の設定を示します．IPアドレスの設定は別の所でやります．

すべてのインターフェースでIS-ISを有効にします．IS-ISでMPLS-TEとSR-MPLSの設定を行います．
NodeSIDの設定でのindexは100単位で与えます．

```
interface r1_h1
  ip router isis 1
interface r1_r2
  ip router isis 1
interface lo
  ip address 10.0.0.1/32
  ip router isis 1
router isis 1
  net 49.0000.0000.0000.0001.00
  is-type level-1
  mpls-te on
  mpls-te router-address 10.0.0.1
  segment-routing on
  segment-routing global-block 16000 23999
  segment-routing node-msd 8
  segment-routing prefix 10.0.0.1/32 index 100
exit
```

<details><summary>R2</summary>

```
interface r2_r1
  ip router isis 1
interface r2_r3
  ip router isis 1
interface r2_r6
  ip router isis 1
interface lo
  ip address 10.0.0.2/32
  ip router isis 1
router isis 1
  net 49.0000.0000.0000.0002.00
  is-type level-1
  mpls-te on
  mpls-te router-address 10.0.0.2
  segment-routing on
  segment-routing global-block 16000 23999
  segment-routing node-msd 8
  segment-routing prefix 10.0.0.2/32 index 200
exit
```
</details>

<details><summary>R3</summary>

```
interface r3_r2
  ip router isis 1
interface r3_r4
  ip router isis 1
interface r3_r5
  ip router isis 1
interface lo
  ip address 10.0.0.3/32
  ip router isis 1
router isis 1
  net 49.0000.0000.0000.0003.00
  is-type level-1
  mpls-te on
  mpls-te router-address 10.0.0.3
  segment-routing on
  segment-routing global-block 16000 23999
  segment-routing node-msd 8
  segment-routing prefix 10.0.0.3/32 index 300
exit
```
</details>

<details><summary>R4</summary>

```
interface r4_r3
  ip router isis 1
interface r4_h3
  ip router isis 1
interface lo
  ip address 10.0.0.4/32
  ip router isis 1
router isis 1
  net 49.0000.0000.0000.0004.00
  is-type level-1
  mpls-te on
  mpls-te router-address 10.0.0.4
  segment-routing on
  segment-routing global-block 16000 23999
  segment-routing node-msd 8
  segment-routing prefix 10.0.0.4/32 index 400
exit
```
</details>

<details><summary>R5</summary>

```
interface r5_h2
  ip router isis 1
interface r5_r6
  ip router isis 1
interface lo
  ip address 10.0.0.5/32
  ip router isis 1
router isis 1
  net 49.0000.0000.0000.0005.00
  is-type level-1
  mpls-te on
  mpls-te router-address 10.0.0.5
  segment-routing on
  segment-routing global-block 16000 23999
  segment-routing node-msd 8
  segment-routing prefix 10.0.0.5/32 index 500
exit
```
</details>

<details><summary>R6</summary>

```
interface r6_r2
  ip router isis 1
interface r6_r5
  ip router isis 1
interface r6_r7
  ip router isis 1
interface lo
  ip address 10.0.0.6/32
  ip router isis 1
router isis 1
  net 49.0000.0000.0000.0006.00
  is-type level-1
  mpls-te on
  mpls-te router-address 10.0.0.6
  segment-routing on
  segment-routing global-block 16000 23999
  segment-routing node-msd 8
  segment-routing prefix 10.0.0.6/32 index 600
exit
```
</details>

<details><summary>R7</summary>

```
interface r7_r3
  ip router isis 1
interface r7_r6
  ip router isis 1
interface r7_r8
  ip router isis 1
interface lo
  ip address 10.0.0.7/32
  ip router isis 1
router isis 1
  net 49.0000.0000.0000.0007.00
  is-type level-1
  mpls-te on
  mpls-te router-address 10.0.0.7
  segment-routing on
  segment-routing global-block 16000 23999
  segment-routing node-msd 8
  segment-routing prefix 10.0.0.7/32 index 700
exit
```
</details>

<details><summary>R8</summary>

```
interface r8_r7
  ip router isis 1
interface r8_h4
  ip router isis 1
interface lo
  ip address 10.0.0.8/32
  ip router isis 1
router isis 1
  net 49.0000.0000.0000.0008.00
  is-type level-1
  mpls-te on
  mpls-te router-address 10.0.0.8
  segment-routing on
  segment-routing global-block 16000 23999
  segment-routing node-msd 8
  segment-routing prefix 10.0.0.8/32 index 800
exit
```
</details>


## トポロジの設定
LinuxでMPLSを扱う際には，インターフェースで有効にする必要があります．
```
sudo sysctl -w net.mpls.platform_labels=1048575
sudo sysctl -w net.mpls.conf.<インターフェース>.input=1
```
`ipnet_mininet`では自動で設定されるようにしています．


トポロジの設定を行います．
```python
from mininet.log import setLogLevel

from ipnet import IPNetwork, IPNode, CLIX, MPLSRouter


def setup() -> IPNetwork:
    setLogLevel("info")
    net = IPNetwork()

    # FRRouting Router
    r1 = net.addFRR("r1", enable_daemons=["isisd"], cls=MPLSRouter)
    r2 = net.addFRR("r2", enable_daemons=["isisd"], cls=MPLSRouter)
    r3 = net.addFRR("r3", enable_daemons=["isisd"], cls=MPLSRouter)
    r4 = net.addFRR("r4", enable_daemons=["isisd"], cls=MPLSRouter)
    r5 = net.addFRR("r5", enable_daemons=["isisd"], cls=MPLSRouter)
    r6 = net.addFRR("r6", enable_daemons=["isisd"], cls=MPLSRouter)
    r7 = net.addFRR("r7", enable_daemons=["isisd"], cls=MPLSRouter)
    r8 = net.addFRR("r8", enable_daemons=["isisd"], cls=MPLSRouter)

    # host
    h1: IPNode = net.addHost("h1", cls=IPNode)
    h2: IPNode = net.addHost("h2", cls=IPNode)
    h3: IPNode = net.addHost("h3", cls=IPNode)
    h4: IPNode = net.addHost("h4", cls=IPNode)

    # config link
    net.addLink(r1, h1,
                intfName1="r1_h1", params1={"ip": "192.168.1.1/24"},
                intfName2="h1_r1", params2={"ip": "192.168.1.2/24"})
    net.addLink(r1, r2,
                intfName1="r1_r2", params1={"ip": "192.168.12.1/24"},
                intfName2="r2_r1", params2={"ip": "192.168.12.2/24"})
    net.addLink(r2, r3,
                intfName1="r2_r3", params1={"ip": "192.168.23.1/24"},
                intfName2="r3_r2", params2={"ip": "192.168.23.2/24"})
    net.addLink(r2, r6,
                intfName1="r2_r6", params1={"ip": "192.168.26.1/24"},
                intfName2="r6_r2", params2={"ip": "192.168.26.2/24"})
    net.addLink(r3, r4,
                intfName1="r3_r4", params1={"ip": "192.168.34.1/24"},
                intfName2="r4_r3", params2={"ip": "192.168.34.2/24"})
    net.addLink(r3, r7,
                intfName1="r3_r7", params1={"ip": "192.168.37.1/24"},
                intfName2="r7_r3", params2={"ip": "192.168.37.2/24"})
    net.addLink(r4, h3,
                intfName1="r4_h3", params1={"ip": "192.168.3.1/24"},
                intfName2="h3_r4", params2={"ip": "192.168.3.2/24"})
    net.addLink(r5, h2,
                intfName1="r5_h2", params1={"ip": "192.168.2.1/24"},
                intfName2="h2_r5", params2={"ip": "192.168.2.2/24"})
    net.addLink(r5, r6,
                intfName1="r5_r6", params1={"ip": "192.168.56.1/24"},
                intfName2="r6_r5", params2={"ip": "192.168.56.2/24"})
    net.addLink(r6, r7,
                intfName1="r6_r7", params1={"ip": "192.168.67.1/24"},
                intfName2="r7_r6", params2={"ip": "192.168.67.2/24"})
    net.addLink(r7, r8,
                intfName1="r7_r8", params1={"ip": "192.168.78.1/24"},
                intfName2="r8_r7", params2={"ip": "192.168.78.2/24"})
    net.addLink(r8, h4,
                intfName1="r8_h4", params1={"ip": "192.168.4.1/24"},
                intfName2="h4_r8", params2={"ip": "192.168.4.2/24"})
    
    h1.add_default_route_cmd("h1_r1", "192.168.1.1")
    h2.add_default_route_cmd("h2_r5", "192.168.2.1")
    h3.add_default_route_cmd("h3_r4", "192.168.3.1")
    h4.add_default_route_cmd("h4_r8", "192.168.4.1")
```

その後ネットワークを実行します．詳細は[github](https://github.com/shu1r0/ipnet_mininet)の`examples/mpls/sr_mpls_isis_12.py`を参照．

## 動作確認

SR-MPLSによるネットワークを体験したいので以下のルートを設定しておきます．
```python
r1.cmd("ip route add 192.168.2.2/32 encap mpls 16400/16800/16500 via 192.168.12.2")
r1.cmd("ip route add 192.168.3.2/32 encap mpls 16500/16800/16400 via 192.168.12.2")
r1.cmd("ip route add 192.168.4.2/32 encap mpls 16400/16500/16800 via 192.168.12.2")
```
例えばh1からh2にpingするときには，r4とr8を経由するように遠回りさせます．
![sr_mpls_isis_12-route1.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2661127/b5e3d13d-2046-3d2a-070a-3b39d579f542.png)



### R1のルーティングテーブル
ルーティングテーブルを確認すると，IS-ISでルートが学習できていることがわかります．
![screenshot000731.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2661127/a6f1dfa5-df31-1254-6c6d-6e5c0988aa97.jpeg)

### R1のMPLSテーブル
![screenshot000732.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2661127/5d72beb1-d198-44b2-0736-f67b3894a1d7.jpeg)


### h1 -> h2のping
h1からh2へpingを実行します．その後，パケットを確認します．
![screenshot000735.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2661127/800a5973-604f-83a9-8231-cfead5ca5f41.jpeg)

`r1_r2`でのパケットです．
![screenshot000736.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2661127/014352c2-1446-1ca9-19a7-d15fa16105d6.jpeg)

`16400`，`16800`，`16500`のラベルが積まれていることがわかります．


`r4_r3`でのパケットでは，上のラベルから`16400`がpopされていることがわかります．
![screenshot000737.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2661127/20d6b34f-805a-1165-37e5-bb35ebc4ed9b.jpeg)

## おわりに
以上，SR-MPLSを試してみた話でした．

## 参考文献
* [FRRouting SR Segment Routing tech demo](http://www.uni-koeln.de/~pbogusze/posts/FRRouting_SR_Segment_Routing_tech_demo.html)
* [ISIS — FRR latest documentation](https://docs.frrouting.org/en/latest/isisd.html)
* [Use Linux as an MPLS Router | Drown in Codes](https://blog.swineson.me/en/use-linux-as-an-mpls-router/)
* [MPLS網にSegment Routingを適用してみよう ①基本設定とFRR | ネットワンシステムズ](https://www.netone.co.jp/knowledge-center/blog-column/knowledge_takumi_175/)
* [FRRoutingでSR-MPLSを動かす](https://qiita.com/watal/items/754f120f6066c2bb0064)