Ryu SDN Frameworkを活用した簡易ルータ機能の作成(7) 〜IPルーティング基本編〜
前回、疎通診断編では、以下の課題に直面してしまいました。
- 前回の疎通診断の実装課題
疎通診断先のアドレス情報を特定するには、OpenflowRouter内部でもルーティングテーブル,ARPテーブルとMACテーブルを保持することが必須となりますが、まだ、これらテーブルの実装までには至っていませんので、REST-IF上で疎通診断で必要になるアドレス情報をすべて指定する方式としました。
そこで、今回は、こちらの実装課題に対応すべく、Openflow簡易ルータ機能のリファクタリングを実施して、IPルーティングの基本編とも言える、「デフォルトルーティング」にチャレンジしたいと思います。
◆IPルーティング機能
まず、OpenFlowコントローラで、ARP情報、Interfacej情報(Portテーブル)を保持できるように、前回までOpenFlow簡易ルータのリファクタリングから着手しました。さらに、IPルーティングでよく活用されているデフォルトルート設定をOpenFlowルータのFlowエントリで保持できるようにGateway機能を実現しました。実際の機能構成イメージは、概ね、こんな感じです
実装コードは、こちらになります。
前回までのRESTインタフェース(openflowRouter)の実装コード拡張
前回までのSimpleRouterの実装コード拡張
◆実際に動かしてみた
OpenFlowスイッチに接続したPC端末等(HOST1, HOST2, vyatta)でIP通信を行ってみました。
1. 事前準備
Ryuアプリ「openflowRouter」の起動後、OpenFlowスイッチのインタフェース情報(PortTable)とGateway情報(デフォルトルート)の設定を行います。操作手順は、以下のとおりです。
(操作手順1)インタフェース設定
RESTインタフェースでインタフェース設定が簡易に行えるよう、RESTクライアント側をスクリプト化しました。
RESTクライアント(インタフェース情報)の設定
$ ./post_interface.sh ====================================================================== create_interface ====================================================================== /openflow/0000000000000001/interface { "interface": { "port": "1", "macaddress": "00:00:00:00:00:01", "ipaddress": "192.168.0.10", "opposite_ipaddress": "192.168.0.1" } } ---------- reply: 'HTTP/1.1 200 OK\r\n' header: Content-Type: application/json; charset=UTF-8 header: Content-Length: 155 header: Date: Sun, 16 Feb 2014 08:33:36 GMT ---------- { "interface": { "macaddress": "00:00:00:00:00:01", "ipaddress": "192.168.0.10", "port": "1", "opposite_ipaddress": "192.168.0.1" }, "id": "0000000000000001" } ====================================================================== create_interface ====================================================================== /openflow/0000000000000001/interface { "interface": { "port": "2", "macaddress": "00:00:00:00:00:02", "ipaddress": "192.168.1.10", "opposite_ipaddress": "192.168.1.1" } } ---------- reply: 'HTTP/1.1 200 OK\r\n' header: Content-Type: application/json; charset=UTF-8 header: Content-Length: 155 header: Date: Sun, 16 Feb 2014 08:33:46 GMT ---------- { "interface": { "macaddress": "00:00:00:00:00:02", "ipaddress": "192.168.1.10", "port": "2", "opposite_ipaddress": "192.168.1.1" }, "id": "0000000000000001" }
さきほど設定したインタフェース情報を簡単に確認できるよう、RESTクライアント側をスクリプト化しました。
RESTクライアントでのインタフェース確認
$ ./get_interface.sh /openflow/0000000000000001/interface ----------------------------------------------------------- reply: 'HTTP/1.1 200 OK\r\n' header: Content-Type: application/json; charset=UTF-8 header: Content-Length: 250 header: Date: Sun, 16 Feb 2014 08:34:16 GMT +++++++++++++++++++++++++++++++ 2014/02/16 17:34:16 : PortTable +++++++++++++++++++++++++++++++ portNo IpAddress MacAddress -------- ------------ ----------------- 1 192.168.0.10 00:00:00:00:00:01 2 192.168.1.10 00:00:00:00:00:02
インタフェース設定後に、OpenFlowコントローラが自動で学習するARP情報を簡単に確認できるよう、RESTクライアント側をスクリプト化しました。
RESTクライアントでのARP確認
$ ./get_arp.sh /openflow/0000000000000001/arp ----------------------------------------------------------- reply: 'HTTP/1.1 200 OK\r\n' header: Content-Type: application/json; charset=UTF-8 header: Content-Length: 234 header: Date: Sun, 16 Feb 2014 08:34:24 GMT +++++++++++++++++++++++++++++++ 2014/02/16 17:34:24 : ArpTable +++++++++++++++++++++++++++++++ portNo MacAddress IpAddress -------- ----------------- ------------ 1 52:54:00:b2:55:2c 192.168.0.1 2 52:54:00:0b:d0:48 192.168.1.1
(操作手順2)Gateway設定
RESTインタフェースでデフォルトルート設定が簡易に行えるよう、RESTクライアント側をスクリプト化しました。
RESTクライアント(Gateway情報)の設定
$ ./post_gateway.sh ====================================================================== create_gateway ====================================================================== /openflow/0000000000000001/gateway { "gateway": { "ipaddress": "192.168.0.1" } } ---------- reply: 'HTTP/1.1 200 OK\r\n' header: Content-Type: application/json; charset=UTF-8 header: Content-Length: 67 header: Date: Sun, 16 Feb 2014 08:34:08 GMT ---------- { "id": "0000000000000001", "gateway": { "ipaddress": "192.168.0.1" } }
Ryuアプリ動作結果
参考までに、Ryuアプリ「openflowRouter」を起動したターミナル画面の出力結果を掲載しておきます。
$ ryu-manager openflowRouter.py loading app openflowRouter.py loading app ryu.controller.ofp_handler loading app ryu.controller.ofp_handler creating context wsgi instantiating app None of SimpleMonitor creating context monitor instantiating app openflowRouter.py of OpenflowRouter instantiating app ryu.controller.ofp_handler of OFPHandler (3310) wsgi starting up on http://0.0.0.0:8080/ (3310) accepted ('127.0.0.1', 59422) 127.0.0.1 - - [16/Feb/2014 17:33:36] "POST /openflow/0000000000000001/interface HTTP/1.1" 200 279 0.004262 (3310) accepted ('127.0.0.1', 59423) 127.0.0.1 - - [16/Feb/2014 17:33:46] "POST /openflow/0000000000000001/interface HTTP/1.1" 200 279 0.004959 (3310) accepted ('127.0.0.1', 59424) 127.0.0.1 - - [16/Feb/2014 17:34:08] "POST /openflow/0000000000000001/gateway HTTP/1.1" 200 190 0.000895 (3310) accepted ('127.0.0.1', 59425) +++++++++++++++++++++++++++++++ 2014/02/16 17:34:16 : PortTable +++++++++++++++++++++++++++++++ portNo IpAddress MacAddress -------- ------------ ----------------- 1 192.168.0.10 00:00:00:00:00:01 2 192.168.1.10 00:00:00:00:00:02 127.0.0.1 - - [16/Feb/2014 17:34:16] "GET /openflow/0000000000000001/interface HTTP/1.1" 200 374 0.000790 (3310) accepted ('127.0.0.1', 59426) +++++++++++++++++++++++++++++++ 2014/02/16 17:34:24 : ArpTable +++++++++++++++++++++++++++++++ portNo MacAddress IpAddress -------- ----------------- ------------ 1 52:54:00:b2:55:2c 192.168.0.1 2 52:54:00:0b:d0:48 192.168.1.1 127.0.0.1 - - [16/Feb/2014 17:34:24] "GET /openflow/0000000000000001/arp HTTP/1.1" 200 358 0.000720
2. PC端末からの通信確認
実際に、HOST2からpingを実行してping通信動作を確認しました。
動作結果1「HOST1向けping」
まずは、HOST1に対して、pingを実施しました。OpenFlowスイッチから見た場合、HOST1は2ホップ先になるため、事前に設定しておいたデフォルトルート情報に基づきIPパケットが転送されていることが確認できました。
HOST2上でのターミナル画面
確かに、HOST1向けのpingが成功してますね。
tsubo@Host2:~$ ping 172.16.100.101 PING 172.16.100.101 (172.16.100.101) 56(84) bytes of data. 64 bytes from 172.16.100.101: icmp_req=1 ttl=63 time=1.33 ms 64 bytes from 172.16.100.101: icmp_req=2 ttl=63 time=1.33 ms 64 bytes from 172.16.100.101: icmp_req=3 ttl=63 time=1.31 ms 64 bytes from 172.16.100.101: icmp_req=4 ttl=63 time=1.32 ms 64 bytes from 172.16.100.101: icmp_req=5 ttl=63 time=1.17 ms 64 bytes from 172.16.100.101: icmp_req=6 ttl=63 time=1.05 ms 64 bytes from 172.16.100.101: icmp_req=7 ttl=63 time=0.597 ms 64 bytes from 172.16.100.101: icmp_req=8 ttl=63 time=0.902 ms 64 bytes from 172.16.100.101: icmp_req=9 ttl=63 time=1.40 ms 64 bytes from 172.16.100.101: icmp_req=10 ttl=63 time=1.05 ms ^C --- 172.16.100.101 ping statistics --- 10 packets transmitted, 10 received, 0% packet loss, time 9010ms rtt min/avg/max/mdev = 0.597/1.149/1.406/0.240 ms
OpenFlowスイッチ上のFlowエントリ
HOST1向けのpingパケットが通過したことにより、3行目と5行目のFlowエントリがカウントアップしてますね。
ICMP Echo Requestパケットは、デフォルトルートに対応したFlowエントリに基づき転送されたことが確認できました。(3行目)
一方、ICMP Echo Replyパケットは、connectネットワーク用のFlowエントリに基づき転送されたことが確認できました。(5行目)
ちなみに、OpenFlowコントローラ上から、OpenFlowスイッチのFlowエントリを簡単に確認する方法は、こちらのブログ記事を参考にしてください。
$ ./ssh_login.sh OFPST_FLOW reply (OF1.3) (xid=0x2): cookie=0x0, duration=28.678s, table=0, n_packets=0, n_bytes=0, priority=16,ip,nw_dst=192.168.0.10 actions=CONTROLLER:65535 cookie=0x0, duration=18.704s, table=0, n_packets=0, n_bytes=0, priority=16,ip,nw_dst=192.168.1.10 actions=CONTROLLER:65535 cookie=0x0, duration=16.298s, table=0, n_packets=10, n_bytes=980, priority=1,ip actions=set_field:00:00:00:00:00:01->eth_src,set_field:52:54:00:b2:55:2c->eth_dst,output:1,dec_ttl cookie=0x0, duration=18.634s, table=0, n_packets=0, n_bytes=0, priority=255,ip,in_port=2,dl_src=52:54:00:0b:d0:48,dl_dst=00:00:00:00:00:02,nw_dst=192.168.0.1 actions=set_field:00:00:00:00:00:01->eth_src,set_field:52:54:00:b2:55:2c->eth_dst,output:1,dec_ttl cookie=0x0, duration=18.671s, table=0, n_packets=10, n_bytes=980, priority=255,ip,in_port=1,dl_src=52:54:00:b2:55:2c,dl_dst=00:00:00:00:00:01,nw_dst=192.168.1.1 actions=set_field:00:00:00:00:00:02->eth_src,set_field:52:54:00:0b:d0:48->eth_dst,output:2,dec_ttl cookie=0x0, duration=31.986s, table=0, n_packets=3, n_bytes=126, priority=0 actions=CONTROLLER:65535
動作結果2「vyatta向けping」
まずは、HOST1に対して、pingを実施しました。OpenFlowスイッチから見た場合、vyattaは1ホップ先になるため、事前に設定しておいたconnectネットワーク用Flowエントリに基づきIPパケットが転送されていることが確認できました。
HOST2上でのターミナル画面
こちらも確かに、vyatta向けのpingが成功してますね。
tsubo@Host2:~$ ping 192.168.0.1 PING 192.168.0.1 (192.168.0.1) 56(84) bytes of data. 64 bytes from 192.168.0.1: icmp_req=1 ttl=64 time=0.687 ms 64 bytes from 192.168.0.1: icmp_req=2 ttl=64 time=0.660 ms 64 bytes from 192.168.0.1: icmp_req=3 ttl=64 time=0.812 ms 64 bytes from 192.168.0.1: icmp_req=4 ttl=64 time=0.794 ms 64 bytes from 192.168.0.1: icmp_req=5 ttl=64 time=0.523 ms 64 bytes from 192.168.0.1: icmp_req=6 ttl=64 time=0.560 ms 64 bytes from 192.168.0.1: icmp_req=7 ttl=64 time=0.647 ms 64 bytes from 192.168.0.1: icmp_req=8 ttl=64 time=0.756 ms 64 bytes from 192.168.0.1: icmp_req=9 ttl=64 time=0.975 ms 64 bytes from 192.168.0.1: icmp_req=10 ttl=64 time=0.694 ms ^C --- 192.168.0.1 ping statistics --- 10 packets transmitted, 10 received, 0% packet loss, time 9001ms rtt min/avg/max/mdev = 0.523/0.710/0.975/0.128 ms
OpenFlowスイッチ上のFlowエントリ
vyatta向けのpingパケットが通過したことにより、4行目と5行目のFlowエントリがカウントアップしてますね。
ICMP Echo RequestとEcho Replyパケットともに、connectネットワーク用のFlowエントリに基づき転送されたことが確認できました。
$ ./ssh_login.sh OFPST_FLOW reply (OF1.3) (xid=0x2): cookie=0x0, duration=25.466s, table=0, n_packets=0, n_bytes=0, priority=16,ip,nw_dst=192.168.0.10 actions=CONTROLLER:65535 cookie=0x0, duration=15.459s, table=0, n_packets=0, n_bytes=0, priority=16,ip,nw_dst=192.168.1.10 actions=CONTROLLER:65535 cookie=0x0, duration=12.883s, table=0, n_packets=0, n_bytes=0, priority=1,ip actions=set_field:00:00:00:00:00:01->eth_src,set_field:52:54:00:b2:55:2c->eth_dst,output:1,dec_ttl cookie=0x0, duration=15.417s, table=0, n_packets=10, n_bytes=980, priority=255,ip,in_port=2,dl_src=52:54:00:0b:d0:48,dl_dst=00:00:00:00:00:02,nw_dst=192.168.0.1 actions=set_field:00:00:00:00:00:01->eth_src,set_field:52:54:00:b2:55:2c->eth_dst,output:1,dec_ttl cookie=0x0, duration=15.455s, table=0, n_packets=10, n_bytes=980, priority=255,ip,in_port=1,dl_src=52:54:00:b2:55:2c,dl_dst=00:00:00:00:00:01,nw_dst=192.168.1.1 actions=set_field:00:00:00:00:00:02->eth_src,set_field:52:54:00:0b:d0:48->eth_dst,output:2,dec_ttl cookie=0x0, duration=27.637s, table=0, n_packets=3, n_bytes=126, priority=0 actions=CONTROLLER:65535
動作結果3「OpenFlowスイッチ側ポート向けping(その1)」
さらに、OpenFlowスイッチ側ポートに対して、pingを実施しました。OpenFlowスイッチ側ポートでの受信パケットについては、OpenFlowコントローラにPacket-inする旨のFlowエントリを事前に設定してあります。
OpenFlowコントローラ経由でのICMP Echo Request / Reply通信が確認できました。
HOST2上でのターミナル画面
こちらも、OpenFlowスイッチ側ポート向けのpingが成功してますね。
tsubo@Host2:~$ ping 192.168.0.10 PING 192.168.0.10 (192.168.0.10) 56(84) bytes of data. 64 bytes from 192.168.0.10: icmp_req=1 ttl=64 time=99.7 ms 64 bytes from 192.168.0.10: icmp_req=2 ttl=64 time=101 ms 64 bytes from 192.168.0.10: icmp_req=3 ttl=64 time=2.56 ms 64 bytes from 192.168.0.10: icmp_req=4 ttl=64 time=3.84 ms 64 bytes from 192.168.0.10: icmp_req=5 ttl=64 time=5.52 ms 64 bytes from 192.168.0.10: icmp_req=6 ttl=64 time=6.91 ms 64 bytes from 192.168.0.10: icmp_req=7 ttl=64 time=9.37 ms 64 bytes from 192.168.0.10: icmp_req=8 ttl=64 time=13.7 ms 64 bytes from 192.168.0.10: icmp_req=9 ttl=64 time=15.6 ms 64 bytes from 192.168.0.10: icmp_req=10 ttl=64 time=19.0 ms ^C --- 192.168.0.10 ping statistics --- 10 packets transmitted, 10 received, 0% packet loss, time 9015ms rtt min/avg/max/mdev = 2.566/27.753/101.134/36.686 ms
OpenFlowスイッチ上のFlowエントリ
OpenFlowスイッチ側ポート向けのpingパケットが通過したことにより、1行目のFlowエントリがカウントアップしてますね。
$ ./ssh_login.sh OFPST_FLOW reply (OF1.3) (xid=0x2): cookie=0x0, duration=26.515s, table=0, n_packets=10, n_bytes=980, priority=16,ip,nw_dst=192.168.0.10 actions=CONTROLLER:65535 cookie=0x0, duration=16.499s, table=0, n_packets=0, n_bytes=0, priority=16,ip,nw_dst=192.168.1.10 actions=CONTROLLER:65535 cookie=0x0, duration=13.810s, table=0, n_packets=0, n_bytes=0, priority=1,ip actions=set_field:00:00:00:00:00:01->eth_src,set_field:52:54:00:b2:55:2c->eth_dst,output:1,dec_ttl cookie=0x0, duration=16.456s, table=0, n_packets=0, n_bytes=0, priority=255,ip,in_port=2,dl_src=52:54:00:0b:d0:48,dl_dst=00:00:00:00:00:02,nw_dst=192.168.0.1 actions=set_field:00:00:00:00:00:01->eth_src,set_field:52:54:00:b2:55:2c->eth_dst,output:1,dec_ttl cookie=0x0, duration=16.496s, table=0, n_packets=0, n_bytes=0, priority=255,ip,in_port=1,dl_src=52:54:00:b2:55:2c,dl_dst=00:00:00:00:00:01,nw_dst=192.168.1.1 actions=set_field:00:00:00:00:00:02->eth_src,set_field:52:54:00:0b:d0:48->eth_dst,output:2,dec_ttl cookie=0x0, duration=33.504s, table=0, n_packets=2, n_bytes=84, priority=0 actions=CONTROLLER:65535
動作結果4「OpenFlowスイッチ側ポート向けping(その2)」
もう一方のOpenFlowスイッチ側ポートに対して、pingを実施しました。こちらの場合も同様に、OpenFlowスイッチ側ポートでの受信パケットについては、OpenFlowコントローラにPacket-inする旨のFlowエントリを事前に設定してあります。
OpenFlowコントローラ経由でのICMP Echo Request / Reply通信が確認できました。
HOST2上でのターミナル画面
こちらも、OpenFlowスイッチ側ポート向けのpingが成功してますね。
tsubo@Host2:~$ ping 192.168.1.10 PING 192.168.1.10 (192.168.1.10) 56(84) bytes of data. 64 bytes from 192.168.1.10: icmp_req=1 ttl=64 time=12.1 ms 64 bytes from 192.168.1.10: icmp_req=2 ttl=64 time=16.1 ms 64 bytes from 192.168.1.10: icmp_req=3 ttl=64 time=18.9 ms 64 bytes from 192.168.1.10: icmp_req=4 ttl=64 time=20.7 ms 64 bytes from 192.168.1.10: icmp_req=5 ttl=64 time=23.0 ms 64 bytes from 192.168.1.10: icmp_req=6 ttl=64 time=26.7 ms 64 bytes from 192.168.1.10: icmp_req=7 ttl=64 time=28.6 ms 64 bytes from 192.168.1.10: icmp_req=8 ttl=64 time=30.5 ms 64 bytes from 192.168.1.10: icmp_req=9 ttl=64 time=32.1 ms 64 bytes from 192.168.1.10: icmp_req=10 ttl=64 time=33.2 ms ^C --- 192.168.1.10 ping statistics --- 10 packets transmitted, 10 received, 0% packet loss, time 9012ms rtt min/avg/max/mdev = 12.140/24.229/33.222/6.802 ms
OpenFlowスイッチ上のFlowエントリ
OpenFlowスイッチ側ポート向けのpingパケットが通過したことにより、2行目のFlowエントリがカウントアップしてますね。
$ ./ssh_login.sh OFPST_FLOW reply (OF1.3) (xid=0x2): cookie=0x0, duration=23.888s, table=0, n_packets=0, n_bytes=0, priority=16,ip,nw_dst=192.168.0.10 actions=CONTROLLER:65535 cookie=0x0, duration=13.872s, table=0, n_packets=10, n_bytes=980, priority=16,ip,nw_dst=192.168.1.10 actions=CONTROLLER:65535 cookie=0x0, duration=12.462s, table=0, n_packets=0, n_bytes=0, priority=1,ip actions=set_field:00:00:00:00:00:01->eth_src,set_field:52:54:00:b2:55:2c->eth_dst,output:1,dec_ttl cookie=0x0, duration=13.833s, table=0, n_packets=0, n_bytes=0, priority=255,ip,in_port=2,dl_src=52:54:00:0b:d0:48,dl_dst=00:00:00:00:00:02,nw_dst=192.168.0.1 actions=set_field:00:00:00:00:00:01->eth_src,set_field:52:54:00:b2:55:2c->eth_dst,output:1,dec_ttl cookie=0x0, duration=13.833s, table=0, n_packets=0, n_bytes=0, priority=255,ip,in_port=1,dl_src=52:54:00:b2:55:2c,dl_dst=00:00:00:00:00:01,nw_dst=192.168.1.1 actions=set_field:00:00:00:00:00:02->eth_src,set_field:52:54:00:0b:d0:48->eth_dst,output:2,dec_ttl cookie=0x0, duration=30.156s, table=0, n_packets=3, n_bytes=126, priority=0 actions=CONTROLLER:65535
3. Ryuアプリ「openFlowRouter」からの疎通診断
前回の課題であった点をOpenflowルータのリファクタリング作業に伴い、改善しました。
宛先IPアドレスと自側ポートを指定するのみで疎通診断が実施できるようになりました。
RESTインタフェースで疎通診断が簡易に行えるよう、RESTクライアント側をスクリプト化しました。
RESTクライアントでの疎通診断
$ ./put_ping.sh ====================================================================== ping ====================================================================== /openflow/0000000000000001/ping { "ping": { "hostIp": "172.16.100.101", "data": "Created by Openflow Router", "outPort": "1" } } ---------- reply: 'HTTP/1.1 200 OK\r\n' header: Content-Type: application/json; charset=UTF-8 header: Content-Length: 560 header: Date: Sun, 16 Feb 2014 08:42:56 GMT ---------- { "ping": [ "PING 172.16.100.101 : 26 data bytes", "ping ok ( 34 bytes from 172.16.100.101: icmp_req=1 ttl=63 data=[Created by Openflow Router] )", "ping ok ( 34 bytes from 172.16.100.101: icmp_req=2 ttl=63 data=[Created by Openflow Router] )", "ping ok ( 34 bytes from 172.16.100.101: icmp_req=3 ttl=63 data=[Created by Openflow Router] )", "ping ok ( 34 bytes from 172.16.100.101: icmp_req=4 ttl=63 data=[Created by Openflow Router] )", "ping ok ( 34 bytes from 172.16.100.101: icmp_req=5 ttl=63 data=[Created by Openflow Router] )" ], "id": "0000000000000001" }
◆終わりに
これまでを通じて、OpenFlow簡易ルータの基本的な動作を確認できました。
本来のプログラミングで盛り込まれるべき、バリデーションやエラーハンドリング処理は、割愛してしまいましたが...
次回以降では、実際の家庭内ネットワークにOpenFlowルータを組み込んで、その実用性を体験したいと思います。