パケットが通らない!

概要

あなたはネットワーク開発事業部で働いています。ある日、コーヒーを飲んでいると、あなたの上司は次のようなことをいいました。

今、我が社では、新しい技術を使ってみようという取り組みがあって、Segment Routingっていうのをやってみることになったんだ。だけど、上手く通信ができなくて困っているんだ。君はスーパーエンジニアだって聞いているからこんな問題ちょちょいのちょいだよな!

あなたはスーパーエンジニアなので、この問題を解決してください。

C11, C12, C21, C22

これらの機器は顧客を模した機器です。

ログイン情報

  • ホスト名: 192.168.12.1
  • ユーザ名: user
  • パスワード: ictsc2021

補足

ログイン情報に記されたマシンにログインすると以下のようなプロンプトが表示されます。この画面で1, 2, 3, 4を選択することで所望の機器にログインすることができます。これらの機器は顧客を模した環境なので、設定を変更してはいけません。

R1, R2

これらの機器では、顧客ごとにVPNの設定が行われています。このうち、R1のみが設定変更が可能です。R1, R2はFRRを用いて経路を交換しています。

ルータの初期設定に必要なファイルは、/data/setup-interfaces.sh に保存されており、setup-interfaces.service を用いて設定しています。

ログイン情報

  • ホスト名: 192.168.12.101
  • ユーザ名: user
  • パスワード: ictsc2021

前提条件

  • SRv6以外のプロトコルを利用してL3VPNを実現してはいけません
    • 上司が悲しんでしまいます

初期状態

  • C11 <-> C21, C12 <-> C22でpingを飛ばしても到達しない

終了状態

  • C11 <-> C21, C12 <-> C22でpingが通る
  • C11 <-> C22のように顧客をまたいだ通信ができない
    • 何らかの方法を用いて、顧客の仮想ネットワークが分離されていることを示してください

解説

こんにちは。この問題を作問したえると(proelbtn)です。この問題は、LinuxのネットワークスタックやSegment Routingに関する問題です。

背景

問題文にある通り、今回の問題では「R1, R2間でSegment Routingを使ってL3VPNを張りたいが、設定が上手く行ってないので疎通が取れない」という問題です。文章中のSegment Routingに触れる前に、普通のIPルーティングについて考えてみましょう。通常、ルータは、宛先IPアドレスのみを見てパケットを転送します。そのため、パケットがどこを中継して宛先に転送されるのかを、送信者が決定することはできません。

通常のルーティングに対し、送信者がパケットの経由地を指定することができるソースルーティングという転送方式があります。この方式では、送信者(もしくはパケットの転送者)が「そのパケットが通るべき経由地」をいくつか指定することができます。ソースルーティングには、パケットの経由地を全て指定するようなStrict Source Routingと、パケットの経由地をいくつか指定し、その間がどのように転送されるのかは問わないLoose Source Routingの二種類に分けることができます。そして、Segment Routingは後者のLoose Source Routingの一つです。

Segment Routingでは、Segmentと呼ばれるパケットに対する命令のリストをパケットにくっつけて転送することで、自由にパケットを操作することができるようになります。Segmentには様々な種類がありますが、よく使われるものについてはRFC8986で定義されています。例えば、次のようなものが定義されています。

  • End: 特に何もせずに次の経由地へ転送する
  • End.T: 指定されたIPv6ルーティングテーブルを見てパケットを転送する
  • End.DT4: パケットをdecapsし、指定されたIPv5ルーティングテーブルを見てパケットを転送する

パケットにSegmentのリストを格納する時に、何らかの形式にエンコードして格納する必要があります。MPLSのラベルとしてエンコードするような方式をSR-MPLSと、IPv6のアドレスとしてエンコードするような方式をSRv6と呼びます。SRv6の場合、Segmentの情報は以下のようにしてIPv6のアドレスにエンコードされます。

問題解説

まずはじめに、router01に入り、VRF100のルーティングテーブルを見てみます。すると、172.31.2.0/24は、encap seg6 mode encap segs 1 [ 2001:db8:2::100 ] dev eth1 scope linkという処理をするように設定されています。

user@router01:~$ ip route show vrf vrf100
172.31.1.0/24 dev eth2 proto kernel scope link src 172.31.1.254
172.31.2.0/24  encap seg6 mode encap segs 1 [ 2001:db8:2::100 ] dev eth1 scope link

この処理中の、encap seg6というキーワードで検索をかけると、実際の設定がどのように行われるのか知ることができます。

https://www.apresiatac.jp/blog/20190311947/

先ほどのip routeの結果は、「2001:db8:2::100というセグメントを、Segment Routing Headerに付与してパケットを転送しようとしている」という意味です。ですが、tcpdumpでパケットを確認してみても、パケットが転送されている様子は見られません。

user@router01:~$ sudo tcpdump -ni eth2
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth2, link-type EN10MB (Ethernet), capture size 262144 bytes
11:02:57.970658 IP 172.31.1.1 > 172.31.2.1: ICMP echo request, id 6039, seq 47, length 64
11:02:58.994595 IP 172.31.1.1 > 172.31.2.1: ICMP echo request, id 6039, seq 48, length 64
11:03:00.018665 IP 172.31.1.1 > 172.31.2.1: ICMP echo request, id 6039, seq 49, length 64
user@router01:~$ sudo tcpdump -ni eth1
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth1, link-type EN10MB (Ethernet), capture size 262144 bytes
^C
0 packets captured
0 packets received by filter
0 packets dropped by kernel

カーネルパラメータを確認すると、以下のように、net.ipv4.ip_forwardが0になっています。この状態では、転送されるパケットはdropされてしまいます。同様に、net.ipv6.conf.all.forwardingも0になっています。

user@router01:~$ sudo sysctl -a | grep net.ipv4.ip_forward
net.ipv4.ip_forward = 0
net.ipv4.ip_forward_update_priority = 1
net.ipv4.ip_forward_use_pmtu = 0

そのため、vtyshでFRRに入り、以下のような設定を行います。

user@router01:~$ sudo vtysh

Hello, this is FRRouting (version 7.2.1).
Copyright 1996-2005 Kunihiro Ishiguro, et al.

router01# conf t
router01(config)# ip forwarding
router01(config)# ipv6 forwarding
router01(config)#

この設定を入れると、パケットがSegment Routing Headerでカプセル化されて、eth1から送信されることが確認できます。ですが、この状態ではまだ、C21でパケットを受信しICMP Echo Replyが返るものの、C11でpingの応答を確認することはできません。同様に、C21からC11にpingを打つ場合でも、C11でパケットを受信することはできません。先ほどと同様にtcpdumpをすると、R1のeth1でパケットを受信できていないことが分かります。

R1のbgpdの様子を確認すると、R2から2001:db8:2::/64の経路を受信しています。今回の構成では、LocatorはBGPで広報されていることが分かりますが、R1はR2に2001:db8:1::/64を広報していません。この状態では、R2はパケットを転送することができません。そのため、R1のLocatorを広報するように設定します。

router01# show bgp ipv6 unicast
BGP table version is 2, local router ID is 192.168.12.101, vrf id 0
Default local pref 100, local AS 65001
Status codes:  s suppressed, d damped, h history, * valid, > best, = multipath,
               i internal, r RIB-failure, S Stale, R Removed
Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self
Origin codes:  i - IGP, e - EGP, ? - incomplete

   Network          Next Hop            Metric LocPrf Weight Path
*  2001:db8::/64    fe80::9ea3:baff:fe2c:9be4
                                             0             0 65002 ?
*>                  ::                       0         32768 ?
*> 2001:db8:2::/64  fe80::9ea3:baff:fe2c:9be4
                                             0             0 65002 i

Displayed  2 routes and 3 total paths
user@router01:~$ sudo vtysh

Hello, this is FRRouting (version 7.2.1).
Copyright 1996-2005 Kunihiro Ishiguro, et al.

router01#
router01# conf t
router01(config)# router bgp 65001
router01(config-router)# address-family ipv6 unicast
router01(config-router-af)# network 2001:db8:1::/64

すると、C11から打ったpingの応答がR1のeth1までやってくることが分かります。そして、setup-interfaces.shで設定されたEnd.DT4に従って、パケットがdecapsされ、VRF100に転送されます。しかし、eth2からパケットは送出されません。

ictsc@router01:~$ sudo tcpdump -ni eth1
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth1, link-type EN10MB (Ethernet), capture size 262144 bytes
08:24:54.616136 IP6 2001:db8::1 > 2001:db8:2::100: srcrt (len=2, type=4, segleft=0[|srcrt]
08:24:54.616606 IP6 2001:db8::2 > 2001:db8:1::100: srcrt (len=2, type=4, segleft=0[|srcrt]
08:24:55.640124 IP6 2001:db8::1 > 2001:db8:2::100: srcrt (len=2, type=4, segleft=0[|srcrt]
08:24:55.640715 IP6 2001:db8::2 > 2001:db8:1::100: srcrt (len=2, type=4, segleft=0[|srcrt]
08:24:56.664126 IP6 2001:db8::1 > 2001:db8:2::100: srcrt (len=2, type=4, segleft=0[|srcrt]
08:24:56.664688 IP6 2001:db8::2 > 2001:db8:1::100: srcrt (len=2, type=4, segleft=0[|srcrt]
^C
6 packets captured
6 packets received by filter
0 packets dropped by kernel

ictsc@router01:~$ sudo tcpdump -ni vrf100
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on vrf100, link-type EN10MB (Ethernet), capture size 262144 bytes
08:26:13.464173 IP 172.31.1.1 > 172.31.2.1: ICMP echo request, id 827, seq 2347, length 64
08:26:13.464922 IP 172.31.2.1 > 172.31.1.1: ICMP echo reply, id 827, seq 2347, length 64
08:26:14.488462 IP 172.31.1.1 > 172.31.2.1: ICMP echo request, id 827, seq 2348, length 64
08:26:14.489234 IP 172.31.2.1 > 172.31.1.1: ICMP echo reply, id 827, seq 2348, length 64
08:26:15.512094 IP 172.31.1.1 > 172.31.2.1: ICMP echo request, id 827, seq 2349, length 64
08:26:15.512605 IP 172.31.2.1 > 172.31.1.1: ICMP echo reply, id 827, seq 2349, length 64
^C
6 packets captured
6 packets received by filter
0 packets dropped by kernel

ictsc@router01:~$ sudo tcpdump -ni eth2
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth2, link-type EN10MB (Ethernet), capture size 262144 bytes
08:26:18.584126 IP 172.31.1.1 > 172.31.2.1: ICMP echo request, id 827, seq 2352, length 64
08:26:19.608089 IP 172.31.1.1 > 172.31.2.1: ICMP echo request, id 827, seq 2353, length 64
08:26:20.632100 IP 172.31.1.1 > 172.31.2.1: ICMP echo request, id 827, seq 2354, length 64
08:26:21.656082 IP 172.31.1.1 > 172.31.2.1: ICMP echo request, id 827, seq 2355, length 64
^C
4 packets captured
4 packets received by filter
0 packets dropped by kernel

これは、逆経路フィルターによってパケットが破棄されています。これは「あるパケットの応答は、受信したインターフェースから送られるか」どうかをチェックしています。End.DT4を実行した時点で、入ってきたインターフェースはVRFデバイスになっているものの、172.31.2.0/24の経路はeth1から送り出すというようになっています。このデバイスの不一致が原因でパケットが破棄されています。そのため、逆経路フィルターを無効化する必要があります。

ictsc@router01:~$ ip route show vrf vrf100
172.31.1.0/24 dev eth2 proto kernel scope link src 172.31.1.254
172.31.2.0/24  encap seg6 mode encap segs 1 [ 2001:db8:2::100 ] dev eth1 scope link
sudo sysctl net.ipv4.conf.all.rp_filter=0
sudo sysctl net.ipv4.conf.vrf100.rp_filter=0
sudo sysctl net.ipv4.conf.vrf200.rp_filter=0

すると、パケットが通ることが確認できます。

部分点について

  • net.ipv4.ip_forward, net.ipv6.conf.*.forwardingを有効にする(100点)
  • R1のLocatorを広報する(100点)
  • 逆経路フィルターを無効にする(100点)

感想

Linuxのネットワークスタックの設定のちょっとニッチな部分を突くような問題にしようと思っていたのですが、思いの他、Locatorの広報の部分で引っかかってしまっていて、解いてほしい部分じゃない部分に引っかかりを作ってしまったなとちょっと反省しています。ですが、最初想像していたよりは多くのチームが回答をしてくれていたのでとても嬉しかったです。

参考

  • https://datatracker.ietf.org/doc/html/rfc8402
  • https://www.segment-routing.net/images/201901-SRv6.pdf