Ryu BGPSpeakerの実践活用へのチャレンジ 〜BGP/OpenFlow連携編〜
これまで、OpenFlow簡易ルータ作成を通じて、SDN/OpenFlow的な実装テクニックでも市販ルータ相当なIPルーティング制御が可能であることを確認してきました。しかしながら、これまでのOpenFlow簡易ルータでは、デフォルト・ルーティングしか対応していないため、市販ルータと比べてみるとルーティング機能面で、相当見劣りする現状だと思います。SDN/OpenFlow的な実装テクニックの学習素材と割り切る場合には、これで十分なんでしょうけど。
ところで、最近、Ryu SDN FrameworkにてBGPSpeaker機能が利用できるようになったようです。そこで、OpenFlow簡易ルータでも、BGP機能を取り込んだ上で、ルーティング機能拡充にチャレンジしてみます。
◆まずは、BGPSpeakerを試してみる
Ryu ドキュメントに記載されているBGP Speakerを動かしてみるところからはじめます。
BGP speaker library — Ryu 3.15 documentation
0. 目標感
「家庭LANとPC端末との間を、BGPSpeakerを介して接続した場合、PC端末〜インタネット回線までの途中経路区間でのルーティング情報の配布が正しく行えるかどうかを確認する。」という目標感で、動作環境を構築しました。
1. 動作環境
Ryu SDN FrameworkのBGPSpeaker機能を試してみるにあたり、BGPルータが必要となります。
市販のBGPルータを購入するほど、家計に余力がないので、極力安価に環境整備が実現できる方法として、OpenWRT化したBuffaloルータにQuaggaをインストールして対応しました。
Ryuコントローラは、以下のパッチ導入後のバージョンを配備する必要があります。必要に応じてアップデートを行ってください。
[bgp: bug fix of handling nexthop for eBGP peering · aa497ed · osrg/ryu · GitHub
2. BGPルータ(Quagga1)動作確認
以下のコンフィグ設定を行った上で、BGPテーブルを確認しておきます。
Quagga-1# sh run Building configuration... Current configuration: ! ! password zebra ! interface eth0 ipv6 nd suppress-ra ! interface eth0.1 ip address 192.168.200.1/24 ipv6 nd suppress-ra ! interface eth0.2 ip address 192.168.100.100/24 ipv6 nd suppress-ra ! interface eth0.3 ip address 172.16.101.1/24 ipv6 nd suppress-ra ! interface eth0.4 ip address 172.16.102.1/24 ipv6 nd suppress-ra ! interface eth0.5 ipv6 nd suppress-ra ! interface lo ip address 10.0.0.1/32 ! router bgp 65001 bgp router-id 10.0.0.1 redistribute connected redistribute static neighbor 192.168.200.100 remote-as 65002 ! ip route 0.0.0.0/0 192.168.100.1 ! access-list vty permit 127.0.0.0/8 access-list vty deny any ! ip forwarding ipv6 forwarding ! line vty access-class vty ! end
BGPルータ(Quagga1)では、複数のPrefix経路情報がBGPテーブルに保持されている様子が確認できます。
Quagga-1# sh ip bgp BGP table version is 0, local router ID is 10.0.0.1 Status codes: s suppressed, d damped, h history, * valid, > best, i - internal, r RIB-failure, S Stale, R Removed Origin codes: i - IGP, e - EGP, ? - incomplete Network Next Hop Metric LocPrf Weight Path *> 0.0.0.0 192.168.100.1 0 32768 ? *> 10.0.0.1/32 0.0.0.0 1 32768 ? *> 172.16.101.0/24 0.0.0.0 1 32768 ? *> 172.16.102.0/24 0.0.0.0 1 32768 ? *> 192.168.0.0 0.0.0.0 1 32768 ? *> 192.168.100.0 0.0.0.0 1 32768 ? *> 192.168.200.0 0.0.0.0 1 32768 ? Total number of prefixes 7
3. BGPSpeakerサンプルコード
今回のサンプル(sampleBGP.py)は、こんな感じです。
import eventlet # BGPSpeaker needs sockets patched eventlet.monkey_patch() from ryu.services.protocols.bgp.bgpspeaker import BGPSpeaker def dump_remote_best_path_change(event): print 'the best path changed:', event.remote_as, event.prefix,\ event.nexthop, event.is_withdraw if __name__ == "__main__": speaker = BGPSpeaker(as_number=65002, router_id='10.0.0.2', best_path_change_handler=dump_remote_best_path_change, ssh_console=True) speaker.neighbor_add('192.168.200.1', 65001) prefix = '192.168.101.0/24' speaker.prefix_add(prefix='192.168.101.0/24', next_hop='192.168.201.1') eventlet.sleep(3000)
4. 動作確認結果
「sampleBGP.py」サンプルコードの実行は、ルート権限で行います。
BGPルータ(Quagga1)で保持しているPrefix情報を受信した様子が確認できました。
$ sudo python ./sampleBGP.py the best path changed: 65001 0.0.0.0/0 192.168.200.1 False the best path changed: 65001 10.0.0.1/32 192.168.200.1 False the best path changed: 65001 192.168.200.0/24 192.168.200.1 False the best path changed: 65001 192.168.100.0/24 192.168.200.1 False the best path changed: 65001 192.168.0.0/24 192.168.200.1 False the best path changed: 65001 172.16.102.0/24 192.168.200.1 False the best path changed: 65001 172.16.101.0/24 192.168.200.1 False
再度、BGPルータ(Quagga1)でBGPテーブルを参照してみると、「192.168.101.0」経路が追加されている様子が確認できました。
Quagga-1# sh ip bgp BGP table version is 0, local router ID is 10.0.0.1 Status codes: s suppressed, d damped, h history, * valid, > best, i - internal, r RIB-failure, S Stale, R Removed Origin codes: i - IGP, e - EGP, ? - incomplete Network Next Hop Metric LocPrf Weight Path *> 0.0.0.0 192.168.100.1 0 32768 ? *> 10.0.0.1/32 0.0.0.0 1 32768 ? *> 172.16.101.0/24 0.0.0.0 1 32768 ? *> 172.16.102.0/24 0.0.0.0 1 32768 ? *> 192.168.0.0 0.0.0.0 1 32768 ? *> 192.168.100.0 0.0.0.0 1 32768 ? *> 192.168.101.0 192.168.200.100 0 65002 i *> 192.168.200.0 0.0.0.0 1 32768 ? Total number of prefixes 8
BGPSpeakerでは、豊富なAPIが提供されているので、BGP自体を動作されるのは、それほど難しいことではありません。
でも、BGPSpeakerが、やってくれることは、あくまでもBGP制御によるPrefix経路の配布管理です。
実際に、PC端末がインターネットと通信できるようなデータプレーン構築機能は有しておりません。
そこで、これまでのOpenFlow簡易ルータとのBGP連携により、データプレーン構築を含めた動作確認にチャレンジしてみます。
◆BGP版OpenFlow簡易ルータ構築
PC端末がインターネット通信できるようにするためには、途中経路のデータプレーンを適切に構築する必要があります。そこで、OpenFlow簡易ルータでのBGP/OpenFlow連携の機能拡充を行いました。これにより、BGPSpeakerが対向BGPルータからPrefix経路情報を取得した場合、OpenFlowスイッチ上にFlowエントリを設定できるようになります。
1. プログラムの入手
BGP版OpenFlow簡易ルータのプログラム一式を以下のサイトからダウンロードできます。
Release OpenFlow簡易ルータ(BGP統合版) · ttsubo/simpleRouter · GitHub
あくまでも、BGP/OpenFlow実装テクニックの学習素材なので、実用性を想定した実装は一切行っておりません... (^_^;)
2. 環境構築
実際の環境構築として、OpenFlowスイッチは、RB750GLを使用しました。実際の構築手順は、こちらのブログ記事を参考にしてください。
RouterBOARD (RB750GL)のOpenFlow化へのチャレンジ 〜OpenvSwitch構築手順メモ編〜 - SDN開発エンジニアを目指した活動ブログ
なお、実際のLANケーブル結線は、こんな感じです。
あと、RB750GL上にて、LANケーブル結線に応じたOVSポート追加を行います。
root@OpenWrt:~# ovs-vsctl add-port br0 eth0.4
OpenFlowスイッチでのOpenFlowポート設定は、こんな感じです。
root@OpenWrt:~# ovs-ofctl dump-ports-desc br0 --protocols=OpenFlow13 OFPST_PORT_DESC reply (OF1.3) (xid=0x2): 1(eth0.2): addr:d4:ca:6d:73:14:8b config: 0 state: 0 current: 1GB-FD AUTO_NEG advertised: 1GB-FD supported: 1GB-FD speed: 1000 Mbps now, 1000 Mbps max 2(eth0.3): addr:d4:ca:6d:73:14:8b config: 0 state: 0 current: 1GB-FD AUTO_NEG advertised: 1GB-FD supported: 1GB-FD speed: 1000 Mbps now, 1000 Mbps max 3(eth0.4): addr:d4:ca:6d:73:14:8b config: 0 state: 0 current: 1GB-FD AUTO_NEG advertised: 1GB-FD supported: 1GB-FD speed: 1000 Mbps now, 1000 Mbps max LOCAL(br0): addr:d4:ca:6d:73:14:8b config: PORT_DOWN state: LINK_DOWN speed: 0 Mbps now, 0 Mbps max
つぎに、Ryuコントローラ側の設定です。
まずは、BGPルータとの隣接関係を構築するために、新たに、NIC追加を行い、IPアドレス"192.168.200.100"を付与します。
さらに、OpenFlow簡易ルータ構成に関わるOpenFlow.ini ファイルの初期設定を行います。
"Port1"のmacaddressとして設定する値は、さきほどのIPアドレス"192.168.200.100"として追加したNICに割り当てられているMACアドレスです。動作させる環境に応じて適切なMACアドレスを設定してください。
$ cd simpleRouter-0.2/rest-client/ $ vi OpenFlow.ini [Port1] port = "1" macaddress = "xx:xx:xx:xx:x:xx" ipaddress = "192.168.200.100" netmask = "255.255.255.0" opposite_ipaddress = "192.168.200.1" opposite_asnumber = "65001" port_offload_bgp = "3" [Port2] port = "2" macaddress = "00:00:00:00:00:02" ipaddress = "192.168.201.100" netmask = "255.255.255.0" opposite_ipaddress = "192.168.201.1" opposite_asnumber = "" port_offload_bgp = "" [Gateway] ipaddress = "192.168.200.1"
3. 実行開始
BGP版OpenFlow簡易ルータをルート権限で起動します。
$ sudo ryu-manager openflowRouter.py loading app openflowRouter.py loading app ryu.controller.ofp_handler creating context wsgi instantiating app None of SimpleMonitor creating context monitor instantiating app None of SimpleBGPSpeaker creating context bgps instantiating app openflowRouter.py of OpenflowRouter instantiating app ryu.controller.ofp_handler of OFPHandler API method core.start called with args: {'router_id': '10.0.0.2', 'label_range': (100, 100000), 'waiter': <ryu.lib.hub.Event object at 0x1058a6450>, 'local_as': 65002, 'bgp_server_port': 179, 'refresh_max_eor_time': 0, 'refresh_stalepath_time': 0} (27034) wsgi starting up on http://0.0.0.0:8080/ starting ssh server at localhost:4990
さらに別ターミナルから、REST-IF経由で、構成情報の設定を行います。
$ cd simpleRouter-0.2/rest-client/ $ ./post_interface.sh ====================================================================== create_interface ====================================================================== /openflow/0000000000000001/interface { "interface": { "port": "1", "macaddress": "40:6c:8f:59:31:af", "ipaddress": "192.168.200.100", "netmask": "255.255.255.0", "opposite_ipaddress": "192.168.200.1", "opposite_asnumber": "65001", "port_offload_bgp": "3" } } ---------- reply: 'HTTP/1.1 200 OK\r\n' header: Content-Type: application/json; charset=UTF-8 header: Content-Length: 243 header: Date: Sat, 29 Nov 2014 05:28:47 GMT ---------- { "interface": { "macaddress": "40:6c:8f:59:31:af", "opposite_asnumber": "65001", "port_offload_bgp": "3", "netmask": "255.255.255.0", "opposite_ipaddress": "192.168.200.1", "ipaddress": "192.168.200.100", "port": "1" }, "id": "0000000000000001" } ====================================================================== create_interface ====================================================================== /openflow/0000000000000001/interface { "interface": { "port": "2", "macaddress": "00:00:00:00:00:02", "ipaddress": "192.168.201.100", "netmask": "255.255.255.0", "opposite_ipaddress": "192.168.201.1", "opposite_asnumber": "", "port_offload_bgp": "" } } ---------- reply: 'HTTP/1.1 200 OK\r\n' header: Content-Type: application/json; charset=UTF-8 header: Content-Length: 237 header: Date: Sat, 29 Nov 2014 05:28:52 GMT ---------- { "interface": { "macaddress": "00:00:00:00:00:02", "opposite_asnumber": "", "port_offload_bgp": "", "netmask": "255.255.255.0", "opposite_ipaddress": "192.168.201.1", "ipaddress": "192.168.201.100", "port": "2" }, "id": "0000000000000001" }
最後に、PC端末を収容しているLANセグメントを対象に、スタティックルーティング設定を行います。
$ ./post_route.sh 192.168.101.0 255.255.255.0 192.168.201.1 ====================================================================== create_route ====================================================================== /openflow/0000000000000001/route { "route": { "destination": "192.168.101.0", "netmask": "255.255.255.0", "nexthop": "192.168.201.1" } } ---------- reply: 'HTTP/1.1 200 OK\r\n' header: Content-Type: application/json; charset=UTF-8 header: Content-Length: 125 header: Date: Sat, 29 Nov 2014 05:29:52 GMT ---------- { "route": { "destination": "192.168.101.0", "netmask": "255.255.255.0", "nexthop": "192.168.201.1" }, "id": "0000000000000001" }
OpenFlow簡易ルータで保持しているルーティングテーブル情報を確認してみます。
さきほど設定したスタティックルーティングや、BGPルータから配布されたPrefix情報が確認できました。
$ ./get_route.sh ====================================================================== get_route ====================================================================== /openflow/0000000000000001/route ---------- reply: 'HTTP/1.1 200 OK\r\n' header: Content-Type: application/json; charset=UTF-8 header: Content-Length: 1275 header: Date: Sat, 29 Nov 2014 05:31:28 GMT +++++++++++++++++++++++++++++++ 2014/11/29 14:31:28 : RoutingTable +++++++++++++++++++++++++++++++ prefix nexthop ------------------ ---------------- 192.168.0.0/24 192.168.200.1 192.168.200.0/24 192.168.200.1 0.0.0.0/0 192.168.200.1 10.0.0.1/32 192.168.200.1 172.16.101.0/24 192.168.200.1 192.168.101.0/24 192.168.201.1 172.16.102.0/24 192.168.200.1 192.168.201.1/32 0.0.0.0 192.168.200.1/32 0.0.0.0 192.168.100.0/24 192.168.200.1
BGPルータ側でのBGPテーブルも確認しておきます。
Quagga-1# sh ip bgp BGP table version is 0, local router ID is 10.0.0.1 Status codes: s suppressed, d damped, h history, * valid, > best, i - internal, r RIB-failure, S Stale, R Removed Origin codes: i - IGP, e - EGP, ? - incomplete Network Next Hop Metric LocPrf Weight Path *> 0.0.0.0 192.168.100.1 0 32768 ? *> 10.0.0.1/32 0.0.0.0 1 32768 ? *> 172.16.101.0/24 0.0.0.0 1 32768 ? *> 172.16.102.0/24 0.0.0.0 1 32768 ? *> 192.168.0.0 0.0.0.0 1 32768 ? *> 192.168.100.0 0.0.0.0 1 32768 ? *> 192.168.101.0 192.168.200.100 0 65002 i *> 192.168.200.0 0.0.0.0 1 32768 ? Total number of prefixes 8
OpenFlowスイッチ側でのFlowテーブルも確認しておきます。
root@OpenWrt:~# ovs-ofctl dump-flows br0 --protocols=OpenFlow13 OFPST_FLOW reply (OF1.3) (xid=0x2): cookie=0x0, duration=262.466s, table=0, n_packets=140, n_bytes=52980, priority=15,ip,nw_dst=192.168.101.0/24 actions=set_field:00:00:00:00:00:02->eth_src,set_field:16:23:ec:ab:8d:8d->eth_dst,output:2,dec_ttl cookie=0x0, duration=311.750s, table=0, n_packets=0, n_bytes=0, priority=15,ip,nw_dst=172.16.102.0/24 actions=set_field:40:6c:8f:59:31:af->eth_src,set_field:96:9a:9c:c5:74:70->eth_dst,output:1,dec_ttl cookie=0x0, duration=312.754s, table=0, n_packets=0, n_bytes=0, priority=15,ip,nw_dst=192.168.0.0/24 actions=set_field:40:6c:8f:59:31:af->eth_src,set_field:96:9a:9c:c5:74:70->eth_dst,output:1,dec_ttl cookie=0x0, duration=313.758s, table=0, n_packets=146, n_bytes=11754, priority=15,ip,nw_dst=192.168.100.0/24 actions=set_field:40:6c:8f:59:31:af->eth_src,set_field:96:9a:9c:c5:74:70->eth_dst,output:1,dec_ttl cookie=0x0, duration=310.746s, table=0, n_packets=0, n_bytes=0, priority=15,ip,nw_dst=172.16.101.0/24 actions=set_field:40:6c:8f:59:31:af->eth_src,set_field:96:9a:9c:c5:74:70->eth_dst,output:1,dec_ttl cookie=0x0, duration=314.763s, table=0, n_packets=25, n_bytes=3496, priority=15,ip,nw_dst=192.168.200.0/24 actions=set_field:40:6c:8f:59:31:af->eth_src,set_field:96:9a:9c:c5:74:70->eth_dst,output:1,dec_ttl cookie=0x0, duration=327.431s, table=0, n_packets=129, n_bytes=9677, priority=255,ip,in_port=3,nw_dst=192.168.200.1 actions=output:1 cookie=0x0, duration=327.431s, table=0, n_packets=115, n_bytes=9157, priority=255,ip,in_port=1,nw_dst=192.168.200.100 actions=output:3 cookie=0x0, duration=316.770s, table=0, n_packets=211, n_bytes=31030, priority=1,ip actions=set_field:40:6c:8f:59:31:af->eth_src,set_field:96:9a:9c:c5:74:70->eth_dst,output:1,dec_ttl cookie=0x0, duration=327.432s, table=0, n_packets=0, n_bytes=0, priority=16,ip,nw_dst=192.168.200.100 actions=CONTROLLER:65535 cookie=0x0, duration=315.766s, table=0, n_packets=4, n_bytes=296, priority=15,ip,nw_dst=10.0.0.1 actions=set_field:40:6c:8f:59:31:af->eth_src,set_field:96:9a:9c:c5:74:70->eth_dst,output:1,dec_ttl cookie=0x0, duration=322.417s, table=0, n_packets=0, n_bytes=0, priority=16,ip,nw_dst=192.168.201.100 actions=CONTROLLER:65535 cookie=0x0, duration=330.285s, table=0, n_packets=19, n_bytes=1525, priority=0 actions=CONTROLLER:65535 cookie=0x0, duration=327.431s, table=0, n_packets=7, n_bytes=420, priority=255,arp,in_port=1 actions=output:3,CONTROLLER:65535 cookie=0x0, duration=327.433s, table=0, n_packets=4, n_bytes=240, priority=255,arp,in_port=3 actions=output:1,CONTROLLER:65535 cookie=0x0, duration=274.075s, table=0, n_packets=0, n_bytes=0, priority=2,ip,in_port=2,dl_src=16:23:ec:ab:8d:8d,dl_dst=00:00:00:00:00:02,nw_dst=192.168.200.1 actions=set_field:40:6c:8f:59:31:af->eth_src,set_field:96:9a:9c:c5:74:70->eth_dst,output:1,dec_ttl cookie=0x0, duration=274.075s, table=0, n_packets=0, n_bytes=0, priority=2,ip,in_port=1,dl_src=96:9a:9c:c5:74:70,dl_dst=40:6c:8f:59:31:af,nw_dst=192.168.201.1 actions=set_field:00:00:00:00:00:02->eth_src,set_field:16:23:ec:ab:8d:8d->eth_dst,output:2,dec_ttl
4. 動作確認
ひととおり、PC端末からインターネット通信できるような経路設定が完了したので、さっそく、インターネットにアクセスしてみます。想定どおり、YouTubeを快適にアクセスできました。(BabyMetalのベーシストの超絶テク、いいですよね。)
OpenFlow簡易ルータを通過したトラフィック流量を確認してみました。
$ ./get_flow_stats.sh ====================================================================== get_flowstats ====================================================================== /openflow/0000000000000001/stats/flow ---------- reply: 'HTTP/1.1 200 OK\r\n' header: Content-Type: application/json; charset=UTF-8 header: Content-Length: 1158 header: Date: Sat, 29 Nov 2014 05:46:45 GMT +++++++++++++++++++++++++++++++ 2014/11/29 14:46:45 : FlowStats +++++++++++++++++++++++++++++++ inPort ethSrc ethDst ipv4Dst packets bytes -------- ------------------ ------------------ --------------- -------- -------- 1 96:9a:9c:c5:74:70 40:6c:8f:59:31:af 192.168.201.1 0 0 2 16:23:ec:ab:8d:8d 00:00:00:00:00:02 192.168.200.1 0 0 * * * 192.168.101.0 49038 67791018 * * * 192.168.100.0 363 28728 * * * 172.16.102.0 0 0 * * * 0.0.0.0/0 18161 2768420 * * * 192.168.200.0 49 6952 * * * 10.0.0.1 4 296 * * * 192.168.0.0 0 0 * * * 172.16.101.0 0 0
◆おわりに
SDN/OpenFlow実装テクニックにより、BGP/OpenFlow連携によるデータプレーン構築が可能であることが確認できました。
最近のSDNテクノロジでは、集中制御から分散制御に、話題がシフトしている気がします。
今後は、「SDN分散制御における中核技術としてのBGP拡張が、どこまで具現化していくのか?」を意識しながら、Ryu BGPSpeakerの活用をいろいろと考えていきたいです。