SDN開発エンジニアを目指した活動ブログ

〜SDNなオープンソース製品を実際に使って試してみる〜

Ryu SDN Frameworkを活用した簡易ルータ機能の作成(7) 〜IPルーティング基本編〜

前回、疎通診断編では、以下の課題に直面してしまいました。

- 前回の疎通診断の実装課題
疎通診断先のアドレス情報を特定するには、OpenflowRouter内部でもルーティングテーブル,ARPテーブルとMACテーブルを保持することが必須となりますが、まだ、これらテーブルの実装までには至っていませんので、REST-IF上で疎通診断で必要になるアドレス情報をすべて指定する方式としました。

そこで、今回は、こちらの実装課題に対応すべく、Openflow簡易ルータ機能のリファクタリングを実施して、IPルーティングの基本編とも言える、「デフォルトルーティング」にチャレンジしたいと思います。

◆IPルーティング機能

まず、OpenFlowコントローラで、ARP情報、Interfacej情報(Portテーブル)を保持できるように、前回までOpenFlow簡易ルータのリファクタリングから着手しました。さらに、IPルーティングでよく活用されているデフォルトルート設定をOpenFlowルータのFlowエントリで保持できるようにGateway機能を実現しました。実際の機能構成イメージは、概ね、こんな感じです

f:id:ttsubo:20140216204044j:plain

実装コードは、こちらになります。
前回までの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パケットが転送されていることが確認できました。
f:id:ttsubo:20140216214611j:plain

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パケットが転送されていることが確認できました。
f:id:ttsubo:20140216214634j:plain

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通信が確認できました。
f:id:ttsubo:20140216215209j:plain

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通信が確認できました。
f:id:ttsubo:20140217050437j:plain
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アドレスと自側ポートを指定するのみで疎通診断が実施できるようになりました。
f:id:ttsubo:20140216220040j:plain

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ルータを組み込んで、その実用性を体験したいと思います。