Atom Login Admin

Above the clouds

Ryu コントローラでDiffServ #1 導入編

written on Sunday,December 22,2013

今回はOpenFlow 1.3より追加されたMeter Tableを使ってDiffServを実現する。
準備編として、また少し苦労した点について紹介する。

OpenFlowコントローラはRyuを使用し、ネットワークシュミレーションにはMininetを使用した。
コントローラの実装は開発中のCloudySwitchを使用した。
OpenFlow Switchはofsoftswitch13を使用した。

MininetでOpenFlow SwitchのQueueを使用する為の準備

まず、DiffServベースのQoSをMininetで確認する為にはOpenFlow SwitchのQueueを有効にする必要がある。
ofsoftswitch13を使用する際、MininetではUserSwitchクラスのインスタンスが使われる。ただ、デフォルトでは、ofdatapath コマンドで--no-slicingオプションを設定しているが、Queueを使用するにはslicingを有効にする必要があるので、以下のようにUserSwitchクラスを修正する必要がある。
※インストールし直す必要があるが手っ取り早くUserSwitchのクラスを修正した。

class UserSwitch( Switch ):
    "User-space switch."

    dpidLen = 12

    def __init__( self, name, dpopts='', **kwargs ):
        """Init.
           name: name for the switch
           dpopts: additional arguments to ofdatapath (--no-slicing)"""
        Switch.__init__( self, name, **kwargs )
        pathCheck( 'ofdatapath', 'ofprotocol',
                   moduleName='the OpenFlow reference user switch' +
                              '(openflow.org)' )
        if self.listenPort:
            self.opts += ' --listen=ptcp:%i ' % self.listenPort
        self.dpopts = dpopts
 ---------- Omited ----------

これでdpctlによるQueueの設定ができるようになる。

続いて、トポロジのスクリプトを書く
例として2つのOpenFlow Switchが以下のように接続されているとする。

[Host1] - [OFS 1] - [OFS 2] - [Host 2]

qos_test.py

from mininet.net import Mininet
from mininet.cli import CLI
from mininet.node import UserSwitch as Switch
from mininet.node import RemoteController
from mininet.link import TCLink
from mininet.util import custom

from sample_topology import MyTopo 

def run(net):
    s1 = net.getNodeByName('s1')
    s1.cmdPrint('dpctl unix:/tmp/s1 queue-mod 1 1 1')
    s1.cmdPrint('dpctl unix:/tmp/s1 queue-mod 2 1 1')
    s2 = net.getNodeByName('s2')
    s2.cmdPrint('dpctl unix:/tmp/s2 queue-mod 1 1 1')
    s2.cmdPrint('dpctl unix:/tmp/s2 queue-mod 2 1 1')

def genericTest(topo):
    link = custom(TCLink, bw=5)
    net = Mininet(topo=topo, switch=Switch, link=link, controller=RemoteController)
    net.start()
    run(net)
    CLI(net)
    net.stop()

def main():
    topo = MyTopo()
    genericTest(topo)

ここでは、それぞれのポートにQueue ID 1として1Mbpsをしきい値(minrate)とするQueueを設定した。今回は導入編ということで、Queueを用いたスケジューリングなどは確認できない。Queueを用いてパケットが送出されているところを確認する。

sample_topology.py

from mininet.topo import Topo

LOG = logging.getLogger("sample_topology")

class MyTopo( Topo ):
    "Simple topology example."

    def __init__( self ):
        "Create custom topo."
        # Initialize topology
        Topo.__init__( self )
        # Add hosts and switches
        host01 = self.addHost('h1')
        host02 = self.addHost('h2')
        switch01 = self.addSwitch('s1')
        switch02 = self.addSwitch('s2')
        # Add links
        self.addLink(switch01, switch02, bw=5)
        self.addLink(host01, switch01, bw=5)
        self.addLink(host02, switch02, bw=5)

topos = { 'mytopo': ( lambda: MyTopo() )}

また、Queueを使うActionのエントリは以下の通り設定する必要がある。
以下が例:
順番に注意が必要。先にOFPActionSetQueue、後にOFPActionOutput

actions = [datapath.ofproto_parser.OFPActionSetQueue(1),
                 datapath.ofproto_parser.OFPActionOutput(1)]

Meter Tableのエントリについて

Meter TableのエントリとしてTCPポート5001へのトラフィック流量が1Mbpsを超えたらDSCP値を-1するエントリを設定した。

    def create_meter_entry(self, dp):
        parser = dp.ofproto_parser
        ofproto = dp.ofproto
        band = parser.OFPMeterBandDscpRemark(rate=1000, burst_size=, prec_level=1)
        req = parser.OFPMeterMod(dp, ofproto.OFPMC_ADD, ofproto.OFPMF_KBPS, 1, [band])
        dp.send_msg(req)

ここで、実際にDSCP値がRemarkされてフォワードされるところを確認したかったのだが、
ofsoftswitch13にパッチをあてる必要があった。※Pull Requestは送ってみたが、取り込まれるかは分からない

udatapath/meter_entry.c

            case OFPMBT_DSCP_REMARK:{
                struct ofl_meter_band_dscp_remark *band_header = (struct ofl_meter_band_dscp_remark *)  entry->config->bands[b];
                                // descrease dscp in ipv4 header (Patch)
                struct ip_header *ipv4 = (*pkt)->handle_std->proto->ipv4;
                uint8_t new_dscp = (ipv4->ip_tos >> 5) - band_header->prec_level;
                uint8_t new_tos = (new_dscp << 5 ) | (ipv4->ip_tos & 0x1f);
                uint16_t old_val = htons((ipv4->ip_ihl_ver >> 8) + ipv4->ip_tos);
                uint16_t new_val = htons((ipv4->ip_ihl_ver >> 8) + new_tos);
                ipv4->ip_csum = recalc_csum16(ipv4->ip_csum, old_val, new_val);
                ipv4->ip_tos = new_tos;
                break;
            }

IPv4ヘッダの更新をするので、チェックサム値を再計算する必要がある。

動作確認

DSCP値としてCS4を設定する。1Mbpsを超えた時点でCS3にマーキングされているだろう。

mininet> h2 tshark -i h2-eth0 -w /tmp/h2-eth0.dump &
mininet> h1 iperf -s &
mininet> h2 iperf -c h1 -i 1 -t 10 --tos 128 -M 536
------------------------------------------------------------
Client connecting to 10.0.0.1, TCP port 5001
TCP window size: 85.3 KByte (default)
------------------------------------------------------------
[  3] local 10.0.0.2 port 34874 connected with 10.0.0.1 port 5001
[ ID] Interval       Transfer     Bandwidth
[  3]  0.0- 1.0 sec   640 KBytes  5.24 Mbits/sec
[  3]  1.0- 2.0 sec   512 KBytes  4.19 Mbits/sec
[  3]  2.0- 3.0 sec   640 KBytes  5.24 Mbits/sec
[  3]  3.0- 4.0 sec   512 KBytes  4.19 Mbits/sec
[  3]  4.0- 5.0 sec   512 KBytes  4.19 Mbits/sec
[  3]  5.0- 6.0 sec   512 KBytes  4.19 Mbits/sec
[  3]  6.0- 7.0 sec   512 KBytes  4.19 Mbits/sec
[  3]  7.0- 8.0 sec   640 KBytes  5.24 Mbits/sec
[  3]  8.0- 9.0 sec   512 KBytes  4.19 Mbits/sec
[  3]  9.0-10.0 sec   512 KBytes  4.19 Mbits/sec
[  3]  0.0-10.3 sec  5.50 MBytes  4.48 Mbits/sec
1Mbps未満
Internet Protocol Version 4, Src: 10.0.0.2 (10.0.0.2), Dst: 10.0.0.1 (10.0.0.1)
    Version: 4
    Header length: 20 bytes
    Differentiated Services Field: 0x80 (DSCP 0x20: Class Selector 4; ECN: 0x00: Not-ECT (Not ECN-Capable Transport))
        1000 00.. = Differentiated Services Codepoint: Class Selector 4 (0x20)
        .... ..00 = Explicit Congestion Notification: Not-ECT (Not ECN-Capable Transport) (0x00)

1Mbps超
Internet Protocol Version 4, Src: 10.0.0.2 (10.0.0.2), Dst: 10.0.0.1 (10.0.0.1)
    Version: 4
    Header length: 20 bytes
    Differentiated Services Field: 0x60 (DSCP 0x18: Class Selector 3; ECN: 0x00: Not-ECT (Not ECN-Capable Transport))
        0110 00.. = Differentiated Services Codepoint: Class Selector 3 (0x18)
        .... ..00 = Explicit Congestion Notification: Not-ECT (Not ECN-Capable Transport) (0x00)

Queueの動作確認

mininet> s1 dpctl unix:/tmp/s1 stats-queue

SENDING:
stat_req{type="queue", flags="0x0", port="any", q="all"}

RECEIVED:
stat_repl{type="queue", flags="0x0", stats=[{port="1", q="1", tx_bytes="390328", tx_pkt="5576", tx_err="0"}, {port="2", q="1", tx_bytes="6494322", tx_pkt="11017", tx_err="0"}]}

mininet>

結果

今回は導入編ということで動作確認程度を行ったのだが、少々苦労した。
DiffServのようなQoSフレームワークの実装はまだまだほど遠いが、QoSは、OpenFlowのみせどころとも言えるのでチャレンジしがいがある。
まだ課題は山積みだが、成果が有り次第報告したい。

Comments

Add Comment

Login
This entry was tagged #OpenFlow #Ryu #QoS #Mininet