公開
JANOG53 NETCON 問題解説 (level 4-2):iBGP と LVS
SONiC と cEOS の iBGP・ECMP で到達性を確保し、LVS のセッションパーシステンスで複数の認証接続を同じサーバーへ届ける問題を解説します。

目次
JANOG53 では NETCON の作問や採点回りを担当しました。 こちらのブログでは、level 4-2 の問題について解説します。
level 4-2 問題の概要
トポロジ

問題文
LB01 の配下に認証用の Server を冗長化して設置し、Client01 から LB01 の Frontend IP (10.150.0.100) に対して認証用の通信を行います。
認証用の通信は複数のコネクションを張る必要がありますが、仕様上すべてのコネクションが同じ Backend Server に到達する必要があります。
認証用のプログラムは Client01 の /root/auth.py にあり、 python3 auth.py で実行可能です。
しかし、現在は Client01 から 10.150.0.100 までの到達性がなく、認証に失敗してしまいます。
まずは Client01 から LB01 の Frontend IP (10.150.0.100) への到達性を確保し、その後認証用のプログラムがすべて OK となるよう、ネットワーク構成を変更してください。
達成条件
- Client01 から LB01 の Frontend IP (10.150.0.100) に到達性があること (iBGP を使用し、 ECMP で冗長性を持たせてください。)
- Backend Server の冗長性は維持されていること
- python3 auth.pyの結果がすべて OK となること
制約
- Client01 内の
auth.pyを編集しないでください。(中身の閲覧は可能です) - 各 L3SW および RT 間は iBGP でネイバーを確立してください。
- Server01, Server02にはログインできません
補足事項
- L3SW01 : SONiC
- RT01 : cEOS
- RT02 : cEOS
- L3SW02 : cEOS
- Client01 : Alpine Linux
- LB01 : Alpine Linux で openrc を利用して LVS が稼働
- Server01 : http リクエストを受け付け、Server01 という文字列を返す
- Server02 : http リクエストを受け付け、Server02 という文字列を返す
初期 Config
L3SW01 initial config – /etc/frr/zebra.conf
interface eth1
description To_RT01
ip address 10.11.0.2/30
no shutdown
interface eth2
description To_RT02
ip address 10.21.0.2/30
no shutdown
interface eth3
description To_Client01
ip address 10.100.0.254/24
no shutdown
L3SW01 initial config – /etc/frr/bgpd.conf
router bgp 64512
maximum-paths 100
neighbor 10.11.0.1 remote-as 64512
neighbor 10.11.0.1 update-source eth1
neighbor 10.21.0.1 remote-as 64512
neighbor 10.21.0.1 update-source eth2
network 10.100.0.0/24
L3SW02 initial config
hostname L3SW02
interface Ethernet1
description To_RT01
no switchport
ip address 10.12.0.2/30
no shut
interface Ethernet2
description To_RT02
no switchport
ip address 10.22.0.2/30
no shut
vlan 200
interface Ethernet3
description To_LB01_Backend_Port
switchport mode access
switchport access vlan 200
no shut
interface Ethernet4
description To_LB_Frontend_Port
no switchport
ip address 10.150.0.254/24
no shut
interface Ethernet5
description To_Server01
switchport mode access
switchport access vlan 200
no shut
interface Ethernet6
description To_Server02
switchport mode access
switchport access vlan 200
no shut
interface vlan200
description Vlan200_Gateway
ip add 10.200.0.254/24
no shut
ip routing
router bgp 64512
maximum-paths 100
neighbor 10.12.0.1 remote-as 64512
neighbor 10.12.0.1 update-source Ethernet1
neighbor 10.22.0.1 remote-as 64512
neighbor 10.22.0.1 update-source Ethernet2
network 10.200.0.0/24
RT01 initial config
hostname RT01
interface Ethernet1
description To_L3SW01
no switchport
ip address 10.11.0.1/30
no shut
interface Ethernet2
description To_L3SW02
no switchport
ip address 10.12.0.1/30
no shut
route-map Change_NH_To_10_11_0_1
set ip next-hop 10.11.0.1
route-map Change_NH_To_10_12_0_1
set ip next-hop 10.12.0.1
ip routing
router bgp 64512
neighbor 10.11.0.2 remote-as 64512
neighbor 10.11.0.2 update-source Ethernet1
neighbor 10.12.0.2 remote-as 64512
neighbor 10.12.0.2 update-source Ethernet2
RT02 initial config
hostname RT02
interface Ethernet1
description To_L3SW01
no switchport
ip address 10.21.0.1/30
no shut
interface Ethernet2
description To_L3SW02
no switchport
ip address 10.22.0.1/30
no shut
route-map Change_NH_To_10_21_0_1
set ip next-hop 10.21.0.1
route-map Change_NH_To_10_22_0_1
set ip next-hop 10.22.0.1
ip routing
router bgp 64512
neighbor 10.21.0.2 remote-as 64512
neighbor 10.21.0.2 update-source Ethernet1
neighbor 10.22.0.2 remote-as 64512
neighbor 10.22.0.2 update-source Ethernet2
auth.py
import requests
import sys
auth_url = 'http://10.150.0.100/'
class Authenticator():
def request_authentication(self):
try:
response = requests.get(auth_url, timeout=1)
content_without_empty_lines = response.text.replace('\n', '')
return content_without_empty_lines
except:
print("例外が発生しました。")
print(sys.exc_info())
exit()
def main():
authenticator = Authenticator()
first_result = authenticator.request_authentication()
for i in range(1, 11):
tmp_result = authenticator.request_authentication()
if first_result == tmp_result:
print("Step" + str(i) + " = " + "OK")
else:
print("Step" + str(i) + " = " + "ERROR")
main()
想定解答
① L3SW01 で bgpd を起動する必要があります。
sed -i -e 's/bgpd=no/bgpd=yes/g' /etc/frr/daemons
service frr restart
②L3SW02/RT01/RT02 で iBGP に関する設定を投入する必要があります。
L3SW02
router bgp 64512
network 10.150.0.0/24
RT01
router bgp 64512
neighbor 10.11.0.2 route-reflector-client
neighbor 10.11.0.2 route-map Change_NH_To_10_11_0_1 out
neighbor 10.12.0.2 route-reflector-client
neighbor 10.12.0.2 route-map Change_NH_To_10_12_0_1 out
RT02
router bgp 64512
neighbor 10.21.0.2 route-reflector-client
neighbor 10.21.0.2 route-map Change_NH_To_10_21_0_1 out
neighbor 10.22.0.2 route-reflector-client
neighbor 10.22.0.2 route-map Change_NH_To_10_22_0_1 out
③LB01 でセッションパーシステンスを有効化する必要があります。
ipvsadm -C
ipvsadm -A -t 10.150.0.100:80 -s rr -p
ipvsadm -a -t 10.150.0.100:80 -r 10.200.0.1:80 -m
ipvsadm -a -t 10.150.0.100:80 -r 10.200.0.2:80 -m
rc-service ipvsadm save
rc-service ipvsadm restart
解説
① L3SW01 で bgpd を起動
まずは L3SW01 で bgpd を起動する必要がありました。 これは SONiC では BGP の実装に FRR を利用しているためです。
少し古い部分もありますが、SONiC のアーキテクチャ概要は以下に記載があります。 Architecture · sonic-net/SONiC Wiki (github.com)
② L3SW02・RT01・RT02 で iBGP を設定
以下の 3 点が達成できていれば基本的には点数となったはずです。
- route-reflector-client の設定
- nexthop-self の設定 (route-map でも nexthop-self でも ok)
- 10.150.0.0/24 の広報
認証プログラムの疎通性があったとしても、Static Route での設定や、ECMP で冗長化されていない場合など、減点しています。
③ LB01 でセッションパーシステンスを有効化
アプリケーションによっては異なるセッションが同じ Backend Server に到達する要件を持つ場合があり、それを簡易的に再現させました。
最初は LVS の -p オプションで session persistence を有効化する解答を想定していましたが、scheduling method を sh (source hashing) とする方法も正解としました。
いくつか scheduling method を lblc としている回答がありましたが、サーバーの負荷状況によってはアプリケーションの要件を満たさない場合があると考え、減点しました。
同じ宛先 IP の接続を同じサーバーに割り当てる lblc も、負荷や可用性によって別のサーバーへ振り分けます。詳細は ipvsadm のマニュアルを参照してください。
※ 私も LVS にそこまで明るいわけではないので、見当違いだったら申し訳ありません。。
これらの設定を実施すると、 auth.py の実行結果がすべて ok となります。
おわりに
問題を解いていただいた皆様ありがとうございました。
皆様の解答を拝見して、すこし問題の前提条件が甘かったかなと反省しています。 反省点は次回に活かします。
本問題が SONiC や LVS を知るきっかけになれば幸いです。