今回は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