Atom Login Admin

Above the clouds

Ryu コントローラでMPLSをつかった経路制御 (1)

written on Monday,October 14,2013

Ryu コントローラを使って OpenFlow 1.3 対応のスイッチを開発中だが、MPLSで経路制御がしたかったので、実装してみたが、苦労した。
まずは、MPLSを使った経路制御をOpenFlowで実現する時に苦労した点について紹介したい。
※知識不足によって苦労したとも考えられる。。
また、今回実装したMPLSを使った経路制御の具体的な処理内容については、バグを直しつつ後日少しずつ紹介していきたい。

実装済みのソースコードはCloudySwitchを参照して頂きたい。
OpenFlow SwitchはCPqD ofsoftswitch13を使用している。

まず、単純にPushする場合から考える。

    def _add_flow(self, dp, match, actions):
        inst = [dp.ofproto_parser.OFPInstructionActions(
            dp.ofproto.OFPIT_APPLY_ACTIONS, actions)]

        mod = dp.ofproto_parser.OFPFlowMod(
            dp, cookie=0, cookie_mask=0, table_id=0,
            command=dp.ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0,
            priority=0xff, buffer_id=0xffffffff,
            out_port=dp.ofproto.OFPP_ANY, out_group=dp.ofproto.OFPG_ANY,
            flags=0, match=match, instructions=inst)

        dp.send_msg(mod)

    def test_mpls_flow(self, dp)
        s_label = 90
        match = dp.ofproto_parser.OFPMatch()
        match.set_in_port(in_port)
        f = dp.ofproto_parser.OFPMatchField.make(
            dp.ofproto.OXM_OF_MPLS_LABEL, s_label)
        actions = [dp.ofproto_parser.OFPActionPushMpls(eth_MPLS),
                   dp.ofproto_parser.OFPActionSetField(f),
                   dp.ofproto_parser.OFPActionOutput(out_port, 0)]
        self._add_flow(dp, match, actions)

上記で問題なくPushできるかと思いきや、Flow Modを送信するとエラーが返却される。
※Ryuのテストに於いても上記コードでテストケースが書かれている。
【追記1】
エラー内容:
type=2 (OFPET_BAD_ACTION)
code=10(OFPBAC_MATCH_INCONSISTENT)
Action can't apply for this match,or Set-Field missing prerequisite.

CPqD ofsoftswitch13の仕様は以下となっていることが分かった。

Dpctl can install flows with the MPLS label and TC fields. The first examples shows a flow with both field in the match. To match MPLS fields the ethertype should be present with a value of 0x8847 or 0x8848, if that field is not present the switch returns a bad pre-requisite error.

ただし、調査の結果OFPActionSetFieldでMPLS Lableをセットしなければ、エラーにはならなかった。

    def _add_flow(self, dp, match, actions):
        inst = [dp.ofproto_parser.OFPInstructionActions(
            dp.ofproto.OFPIT_APPLY_ACTIONS, actions)]

        mod = dp.ofproto_parser.OFPFlowMod(
            dp, cookie=0, cookie_mask=0, table_id=0,
            command=dp.ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0,
            priority=0xff, buffer_id=0xffffffff,
            out_port=dp.ofproto.OFPP_ANY, out_group=dp.ofproto.OFPG_ANY,
            flags=0, match=match, instructions=inst)

        dp.send_msg(mod)

    def test_mpls_flow(self, dp)
        match = dp.ofproto_parser.OFPMatch()
        match.set_in_port(in_port)
        actions = [dp.ofproto_parser.OFPActionPushMpls(eth_MPLS),
                   dp.ofproto_parser.OFPActionOutput(out_port, 0)]
        self._add_flow(dp, match, actions)

以下の通りにMPLS Labelを付与してPushできているが、
labelの値が指定できていない。

 -------- Omitted  -------- 
Ethernet II, Src: 52:3f:1f:a5:60:d5 (52:3f:1f:a5:60:d5), Dst: 66:f8:7d:64:74:c8 (66:f8:7d:64:74:c8)
    Destination: 66:f8:7d:64:74:c8 (66:f8:7d:64:74:c8)
        Address: 66:f8:7d:64:74:c8 (66:f8:7d:64:74:c8)
        .... ..1. .... .... .... .... = LG bit: Locally administered address (this is NOT the factory default)
        .... ...0 .... .... .... .... = IG bit: Individual address (unicast)
    Source: 52:3f:1f:a5:60:d5 (52:3f:1f:a5:60:d5)
        Address: 52:3f:1f:a5:60:d5 (52:3f:1f:a5:60:d5)
        .... ..1. .... .... .... .... = LG bit: Locally administered address (this is NOT the factory default)
        .... ...0 .... .... .... .... = IG bit: Individual address (unicast)
    Type: MPLS label switched packet (0x8847)
MultiProtocol Label Switching Header, Label: 0 (IPv4 Explicit-Null), Exp: 0, S: 1, TTL: 64
    0000 0000 0000 0000 0000 .... .... .... = MPLS Label: IPv4 Explicit-Null (0)
    .... .... .... .... .... 000. .... .... = MPLS Experimental Bits: 0
    .... .... .... .... .... ...1 .... .... = MPLS Bottom Of Label Stack: 1
    .... .... .... .... .... .... 0100 0000 = MPLS TTL: 64
Internet Protocol Version 4, Src: 10.0.0.2 (10.0.0.2), Dst: 10.0.0.3 (10.0.0.3)
    Version: 4
    Header length: 20 bytes
    Differentiated Services Field: 0x00 (DSCP 0x00: Default; ECN: 0x00: Not-ECT (Not ECN-Capable Transport))
        0000 00.. = Differentiated Services Codepoint: Default (0x00)
        .... ..00 = Explicit Congestion Notification: Not-ECT (Not ECN-Capable Transport) (0x00)
 -------- Omitted  -------- 

ただ、MPLS Labelを付与する為に必要なマッチのeth_typeが0x8847 or 0x8848という条件はクリアできる。解決方法は、パイプライン処理を使用することだった。

    def test_mpls_flow(self, dp)
        parser = dp.ofproto_parser
        ofproto = dp.ofproto
        eth_MPLS = ether.ETH_TYPE_MPLS
        match = parser.OFPMatch(in_port=in_port, eth_type=eth_MPLS)
        actions = [parser.OFPActionPushMpls(eth_MPLS),
                   parser.OFPActionSetField(mpls_label=94),
                   parser.OFPActionOutput(out_port, 0)]
        insts = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS,
                                              actions)]
        flow_mod = self.create_flow_mod(dp, 0, 1, match, insts)
        dp.send_msg(flow_mod)

        match = parser.OFPMatch(in_port=in_port)
        actions = [parser.OFPActionPushMpls(eth_MPLS)]
        insts = [dp.ofproto_parser.OFPInstructionActions(
                 dp.ofproto.OFPIT_APPLY_ACTIONS, actions),
                 dp.ofproto_parser.OFPInstructionGotoTable(1)] 
        flow_mod = self.create_flow_mod(dp, 0, 0, match, insts) 
        dp.send_msg(flow_mod)

まず、table_id 1のフローをエントリする。table_id 0でMPLSヘッダが付加されるているので、マッチはeth_type 0x8847でin_portは任意のポートとする。
※in_portは必要に応じて

-------- Omitted  -------- 
Ethernet II, Src: 46:cf:88:b1:4b:5f (46:cf:88:b1:4b:5f), Dst: d6:b8:78:c2:49:01 (d6:b8:78:c2:49:01)
    Destination: d6:b8:78:c2:49:01 (d6:b8:78:c2:49:01)
        Address: d6:b8:78:c2:49:01 (d6:b8:78:c2:49:01)
        .... ..1. .... .... .... .... = LG bit: Locally administered address (this is NOT the factory default)
        .... ...0 .... .... .... .... = IG bit: Individual address (unicast)
    Source: 46:cf:88:b1:4b:5f (46:cf:88:b1:4b:5f)
        Address: 46:cf:88:b1:4b:5f (46:cf:88:b1:4b:5f)
        .... ..1. .... .... .... .... = LG bit: Locally administered address (this is NOT the factory default)
        .... ...0 .... .... .... .... = IG bit: Individual address (unicast)
    Type: MPLS label switched packet (0x8847)
MultiProtocol Label Switching Header, Label: 94, Exp: 0, S: 0, TTL: 64
    0000 0000 0000 0101 1110 .... .... .... = MPLS Label: 94
    .... .... .... .... .... 000. .... .... = MPLS Experimental Bits: 0
    .... .... .... .... .... ...0 .... .... = MPLS Bottom Of Label Stack: 0
    .... .... .... .... .... .... 0100 0000 = MPLS TTL: 64
MultiProtocol Label Switching Header, Label: 0 (IPv4 Explicit-Null), Exp: 0, S: 1, TTL: 64
    0000 0000 0000 0000 0000 .... .... .... = MPLS Label: IPv4 Explicit-Null (0)
    .... .... .... .... .... 000. .... .... = MPLS Experimental Bits: 0
    .... .... .... .... .... ...1 .... .... = MPLS Bottom Of Label Stack: 1
    .... .... .... .... .... .... 0100 0000 = MPLS TTL: 64
Internet Protocol Version 4, Src: 10.0.0.2 (10.0.0.2), Dst: 10.0.0.3 (10.0.0.3)
    Version: 4
    Header length: 20 bytes
    Differentiated Services Field: 0x00 (DSCP 0x00: Default; ECN: 0x00: Not-ECT (Not ECN-Capable Transport))
        0000 00.. = Differentiated Services Codepoint: Default (0x00)
        .... ..00 = Explicit Congestion Notification: Not-ECT (Not ECN-Capable Transport) (0x00)
-------- Omitted  -------- 

ラベルは正しく設定されているようだが、スタックされている。
期待する動作ではあるが、ここではSwapさせたい。
SwapさせるにはApply-ActionsでPopし、Write-ActionでアクションセットにPushを設定しておくことで解決できる。(アクションセットはパケットアウトするとき処理される)
※実装はこれが正解でないことを留意していただきたい。

    def test_mpls_flow(self, dp)
        parser = dp.ofproto_parser
        ofproto = dp.ofproto
        eth_MPLS = ether.ETH_TYPE_MPLS
        eth_IP = ether.ETH_TYPE_IP
        match = parser.OFPMatch(in_port=in_port, eth_type=eth_MPLS)
        actions = [parser.OFPActionPushMpls(eth_MPLS),
                   parser.OFPActionSetField(mpls_label=102),
                   parser.OFPActionOutput(out_port, 0)]
        actions_apply = [parser.OFPActionPopMpls(eth_IP)]
        insts = [parser.OFPInstructionActions(ofproto.OFPIT_WRITE_ACTIONS,
                                              actions),
                 parser.OFPInstructionActions(dp.ofproto.OFPIT_APPLY_ACTIONS,
                                              actions_apply)]
        flow_mod = self.create_flow_mod(dp, 0, 1, match, insts)
        dp.send_msg(flow_mod)

        match = parser.OFPMatch(in_port=in_port)
        actions = [parser.OFPActionPushMpls(eth_MPLS)]
        insts = [dp.ofproto_parser.OFPInstructionActions(
                 dp.ofproto.OFPIT_APPLY_ACTIONS, actions),
                 dp.ofproto_parser.OFPInstructionGotoTable(1)] 
        flow_mod = self.create_flow_mod(dp, 0, 0, match, insts) 
        dp.send_msg(flow_mod)

意図したラベルのみが設定されている。

-------- Omitted  -------- 
Ethernet II, Src: b2:82:1c:f3:bc:82 (b2:82:1c:f3:bc:82), Dst: fe:ee:60:5d:46:fa (fe:ee:60:5d:46:fa)
    Destination: fe:ee:60:5d:46:fa (fe:ee:60:5d:46:fa)
        Address: fe:ee:60:5d:46:fa (fe:ee:60:5d:46:fa)
        .... ..1. .... .... .... .... = LG bit: Locally administered address (this is NOT the factory default)
        .... ...0 .... .... .... .... = IG bit: Individual address (unicast)
    Source: b2:82:1c:f3:bc:82 (b2:82:1c:f3:bc:82)
        Address: b2:82:1c:f3:bc:82 (b2:82:1c:f3:bc:82)
        .... ..1. .... .... .... .... = LG bit: Locally administered address (this is NOT the factory default)
        .... ...0 .... .... .... .... = IG bit: Individual address (unicast)
    Type: MPLS label switched packet (0x8847)
MultiProtocol Label Switching Header, Label: 102, Exp: 0, S: 1, TTL: 64
    0000 0000 0000 0110 0110 .... .... .... = MPLS Label: 102
    .... .... .... .... .... 000. .... .... = MPLS Experimental Bits: 0
    .... .... .... .... .... ...1 .... .... = MPLS Bottom Of Label Stack: 1
    .... .... .... .... .... .... 0100 0000 = MPLS TTL: 64
Internet Protocol Version 4, Src: 10.0.0.2 (10.0.0.2), Dst: 10.0.0.3 (10.0.0.3)
    Version: 4
    Header length: 20 bytes
    Differentiated Services Field: 0x00 (DSCP 0x00: Default; ECN: 0x00: Not-ECT (Not ECN-Capable Transport))
        0000 00.. = Differentiated Services Codepoint: Default (0x00)
        .... ..00 = Explicit Congestion Notification: Not-ECT (Not ECN-Capable Transport) (0x00)
-------- Omitted  -------- 

これでMPLSを使った経路制御も可能なのであるが、
考えたくないが、使用するスイッチによってここらへんの仕様が異なっているなんてことも
少し懸念しているのだが、これはまだ未調査。
いずれにしても、試行錯誤して解決できる、
自由度が高いのがOpenFlowの魅力とも言えるのかなと思ったりもした。
さらに、効率の良い方法があればご教授頂きたいところでもある。
当方MPLSにも疎いのでツッコミも頂きたい所存。

次回は実際にMPLSを使って経路制御を行っているところを
解説したい。
開発中のスイッチで実装しているので、バグを直しつつ
完成系に近づけていきたい。

【追記2】
OpenFlow 1.3.0の仕様書に記載されていたようだ。
A.2.3.6 Flow Match Field Prerequisite

The presence of an OXM TLV with a given oxm_type may be restricted based on the presence or values of other OXM TLVs. In general, matching header fields of a protocol can only be done if the OpenFlow match explitly matches the corresponding protocol.

【追記3】
OFPActionSetFieldでeth_type=0x8847を設定すればMPLS Labelの設定もうまくいくのではないかと考えて、実施してみたが、エラーになった。(OFPET_BAD_ACTION)

【追記4】
OFPActionSetFieldでeth_type=0x8847を行った後にPushしてみたが、MPLSヘッダが
スタックされてしまった。従ってPushする前にPopしてみたが、パケットが壊れた。
今のところ上記の方法でしかうまくいっていない。。

Comments

Add Comment

Login
This entry was tagged #Ryu #OpenFlow #SDN