SDN実験4

1 実験テーマ

あなたが 1972 年に住んで ARPAnet を維持していたネットワーク管理者であると仮定します。前の実験では、最短パスを確立する方法を学習し、SDC から MIT への最小ホップ数のパス (図の緑色のパス) を発行しました。 。ある日、同僚のボブは、さらなる調査のため、ユタ州とイリノイ間のすべてのトラフィックが TINKER に展開されたトラフィック アナライザーを通過する必要があるという新しい要件を受け取りました。不注意なボブは、現在のネットワーク ステータスを確認せずにすぐにダウンしました。新しいパス (赤いパス) を送信します。写真の中の)。賢くて機知に富んだあなたは、ボブが発行したフロー テーブルが転送ループを引き起こす可能性があることにすぐに気づきました。

ここで、VeriFlow ツールを実行し、上記の 2 つの転送パスを確認し、実験の次の 2 つの部分を完了する必要があります。

  • 基礎実験部
  1. 影響を受けるECの数を毎回出力する
  2. ループパスに関する情報を出力します。
  3. さらにループに対応するECの関連情報を出力します。
  4. 元のコードとパッチコードの違いを分析し、なぜパッチを追加する必要があるのか​​を考える
  • 実験部分を拡張する
  1. waypoint_path.pyコード内で追加したルールの優先度フィールドを変更すると、VeriFlowの検出結果がおかしくなります。エラーの内容とエラーの理由を説明してください。
  2. VeriFlowがサポートする1​​4ドメインのうち、検証対象となるドメインを複数(5つ以上)選択し、結果を出力・分析します。
  • 実験データダウンロード
    https://www.aliyundrive.com/s/iA9A9BWijz7

2 実験内容

2.1 準備

2.1.1 転送ループの問題を観察する

  • 最短経路制御プログラムを起動する
    ryu-manager ofctl_rest.py shortest_path.py --observe-links

  • トポロジを開始します

    sudo python Arpanet19723.py

  • トポロジでは、SDC が MIT に ping を送信して接続を確立します。

    SDC ping MIT

  • TINKER経由でユタ州からイリノイ州へのルートを発行します

    sudo python waypoint_path.py

  • トポロジでは、SDC が MIT に再度 ping を送信して接続を確立します

    SDC ping MIT

  • USC のフロー テーブルなど、パス上の特定のスイッチを表示します

    sudo ovs-ofctl dump-flows s22

  • Wiresharkを開いてポートを観察します

2.1.2 VeriFlowの使用

  • github から VeriFlow をダウンロードし、実験的なパッチを適用します。

    git clone https://github.com/samueljero/BEADS.git 
    cd BEADS 
    git am 0001-for-xjtu-sdn-exp-2020.patch 
    
  • VeriFlow をコンパイルする

    cd veriflow/VeriFlow 
    make clean all 
    
  • カスタムポートで送信機を開き、最短のプログラムを実行します。

    ryu-manager ofctl_rest.py shortest_path.py --ofp-tcp-listen-port 1998 --observe-links

  • VeriFlowをプロキシモードで実行する

    ./VeriFlow 6633 127.0.0.1 1998 Arpanet19723.txt log_file.txt

  • トポロジを開始します

    sudo python Arpanet19723.py

  • トポロジでは、SDC が MIT に ping を送信して接続を確立します。

    SDC ping MIT

  • TINKER 経由で UTAH から ILLINOIS へのパスを発行し、VeriFlow によって検出されたループ情報をログ ファイルで観察します。

    sudo python waypoint_path.py

2.2 基本実験部

2.2.1 EC番号の印刷

  • VeriFlow::verifyRule()同値クラスの分割、転送グラフの構築、不変式の検証などのVeriFlowコアアルゴリズムを実行する機能です。関数内の変数はecCountEC の数です。

  • ecCount必要なのはログ ファイルに出力することだけです。

  • VeriFlow::verifyRule()修正する前に:

    if(ecCount == 0)
    {
        fprintf(stderr, "[VeriFlow::verifyRule] Error in rule: %s\n", rule.toString().c_str());
        fprintf(stderr, "[VeriFlow::verifyRule] Error: (ecCount = vFinalPacketClasses.size() = 0). Terminating process.\n");
        exit(1);
    }
    else
    {
        // fprintf(stdout, "\n");
        // fprintf(stdout, "[VeriFlow::verifyRule] ecCount: %lu\n", ecCount);
    }
    
  • VeriFlow::verifyRule()変更後:

    fprintf(fp, "[VeriFlow::verifyRule] verifying this rule: %s\n", rule.toString().c_str());
    if(ecCount == 0)
    {
        fprintf(stderr, "[VeriFlow::verifyRule] Error in rule: %s\n", rule.toString().c_str());
        fprintf(stderr, "[VeriFlow::verifyRule] Error: (ecCount = vFinalPacketClasses.size() = 0). Terminating process.\n");
        exit(1);
    }
    else
    {
        fprintf(stdout, "\n");
        fprintf(stdout, "[VeriFlow::verifyRule] ecCount: %lu\n", ecCount);
        fprintf(fp, "[VeriFlow::verifyRule] ecCount: %lu\n", ecCount);//输出到日志文件
    }
    
  • 再コンパイル、ログファイルのスクリーンショット

2.2.2 ループ経路の印刷

  • VeriFlow::traverseForwardingGraph()特定の EC の転送グラフを走査するには、ループやブラック ホールがあるかどうかを確認します。変数を追加してvector<string> loop_pathループ パスを記録するだけで済みます。

  • VeriFlow::traverseForwardingGraph()変更後:

    bool VeriFlow::traverseForwardingGraph(const EquivalenceClass& packetClass, ForwardingGraph* graph, const string& currentLocation, const string& lastHop, unordered_set< string > visited, FILE* fp,vector<string> loop_path)
    {
    	...
    	if(visited.find(currentLocation) != visited.end())
    	{
    		// Found a loop.
    		fprintf(fp, "\n");
    		fprintf(fp, "[VeriFlow::traverseForwardingGraph] Found a LOOP for the following packet class at node %s.\n", currentLocation.c_str());
    		fprintf(fp, "[VeriFlow::traverseForwardingGraph] PacketClass: %s\n", packetClass.toString().c_str());
    		fprintf(fp, "[VeriFlow::traverseForwardingGraph] Loop path is:\n");
    		bool flag=false;
    		for(unsigned int i = 0; i < loop_path.size()-1; i++) {
    			if(loop_path[i]==currentLocation){
    				flag=true;
    			}
    			if(flag){
    				fprintf(fp, "%s --> ", loop_path[i].c_str());
    			}
    		}
    		fprintf(fp, "%s\n", currentLocation.c_str());
    		for(unsigned int i = 0; i < faults.size(); i++) {
    			if (packetClass.subsumes(faults[i])) {
    				faults.erase(faults.begin() + i);
    				i--;
    			}
    		}
    		faults.push_back(packetClass);
    
    		return false;
    	}
    	visited.insert(currentLocation);
    	loop_path.push_back(currentLocation);
        ...
        return this->traverseForwardingGraph(packetClass, graph, itr->rule.nextHop, currentLocation, visited, fp,loop_path);
    }
    
  • 再コンパイル、ログファイルのスクリーンショット

2.2.3 関連データパッケージ情報の印刷

  • EC の基本情報は 14 ドメインのインターバル形式で表示されますが、Bob のトラブルシューティングを容易にするために EC 情報の表現を簡略化し、14 ドメインから TCP/IP 5 つ組のみを抽出して主な情報表示としています。

  • 既存ECの基本情報印刷コード

    VeriFlow::traverseForwardingGraph():

    fprintf(fp, "[VeriFlow::traverseForwardingGraph] PacketClass: %s\n", packetClass.toString().c_str());

  • EquivalenceClass::toString()関数を模倣し、それに関数EquivalenceClassを追加するTcpIptoString()

    string EquivalenceClass::TcpIptoString() const
    {
    	char buffer[1024];
    	sprintf(buffer, "[ nw_src(%s-%s), nw_dst(%s-%s)",
    			::getIpValueAsString(this->lowerBound[NW_SRC].c_str),
    			::getIpValueAsString(this->upperBound[NW_SRC].c_str),
    			::getIpValueAsString(this->lowerBound[NW_DST].c_str),
    			::getIpValueAsString(this->upperBound[NW_DST].c_str);
    
    	string retVal = buffer;
    	retVal += ", ";
    	sprintf(buffer,"nw_proto(%lu-%lu)",this->lowBound[NW_PROTO],this->upperBound[NW_PROTO]);
    	retVal+=buffer;
    	retVal+=",";
    	sprintf(buffer,"tp_src(%lu-%lu)",this->lowBound[TP_SRC],this->upperBound[TP_SRC]);
    	retVal+=buffer;
    	retVal+=",";
    	sprintf(buffer,"tp_dst(%lu-%lu)",this->lowBound[TP_DST],this->upperBound[TP_DST]);
    	retVal+=buffer;
    	return retVal;
    }
    
  • 再コンパイル、ログファイルのスクリーンショット

2.2.4 オリジナルコードとパッチコードの違いを分析し、なぜパッチを追加する必要があるのか​​を考える

  • ファイルを変更する

    • ベリフロー/VeriFlow/ネットワーク.cpp | 1+

    • veriflow/VeriFlow/OpenFlowProtocolMessage.cpp | 17 +++±–

    • ベリフロー/VeriFlow/Rule.cpp | 8++±

    • ベリフロー/VeriFlow/Rule.h | 1+

    • ベリフロー/ベリフロー/ベリフロー.cpp | 45 ++++++++++++++++±–

    • ベリフロー/ベリフロー/ベリフロー.h |

  • パッチの修正内容と分析を表示する

    • コマンドを使用する

      git diff HEAD origin/HEAD

    • in_portルールに属性を追加し、ペアin_portのストレージと処理を追加しました

    • lastHop を保存、BlackHole を見つける機能が向上

    • ブラックホールの判定状況を改善する

      github のオープン ソース コードでは、ブラック ホールを判断するための 2 つの状況が示されています。

      • 現在のスイッチまたはホストは現在のネットワークにありません
      • 現在のスイッチまたはホストはネットワーク内にありますが、他のスイッチまたはホストへのリンクがありません

      ブラックホールを判定するケースがパッチに追加されました。

      • 現在のスイッチまたはホストにも、ネットワークのトポロジ内で接続されているリンクがありますが、ネットワーク構造の変更により、現在のスイッチまたはホストの位置と対応するポート (in_port) から前のホップを見つけることができません。 .スイッチまたはホスト。

    • ループの判断力向上

      次のホップを選択すると、前のホップと同じことを回避できます。

2.3 実験部分の拡張

waypoint_path.py2.3.1コード内で追加したルールの優先度フィールドを変更すると、VeriFlow の検出結果が間違ってしまうので、エラーの内容とエラーの理由を説明してください

  • コードに追加されたルールの優先度フィールドを 1 に変更するwaypoint_path.pyと、ログ ファイルにループがなく、SDC ping MITブロックできないことがわかります。

  • フローテーブルを監視する

    • 優先度フィールドを変更する前に

      • s22フローテーブル

      • s25フローテーブル

    • 優先度フィールドを変更した後

      • s22フローテーブル

      • s25フローテーブル

    • 一致するフィールドが同じ場合、新しいフロー エントリが古いフロー エントリを覆い、実際にはループが発生しますが、VeriFlow はループを検出しません。

  • ループの原因が特定できません

    • veriflow ではループを判定してネクストホップを選択する際に、優先度フィールドを使用してソートを行うため、同じ優先度のルールが存在する場合に問題が発生します。

      graph->links[currentLocation].sort(compareForwardingLink);

    • ルールが完全に一致し、元のルールを上書きする必要がある場合、VeriFlow は元のルールを削除して新しいルールを追加するのではなく、元のルールを保持し、新しく追加したルールを破棄します。

2.3.2 VeriFlow がサポートする 14 ドメインのうち、検証対象となる複数のドメイン (5 つ以上) を選択し、結果を出力および分析します

  • 確認できるドメイン

    enum FieldIndex
    {
    	IN_PORT, // 0
    	DL_SRC,
    	DL_DST,
    	DL_TYPE,
    	DL_VLAN,
    	DL_VLAN_PCP,
    	MPLS_LABEL,
    	MPLS_TC,
    	NW_SRC,
    	NW_DST,
    	NW_PROTO,
    	NW_TOS,
    	TP_SRC,
    	TP_DST,
    	ALL_FIELD_INDEX_END_MARKER, // 14
    	METADATA, // 15, not used in this version.
    	WILDCARDS // 16
    };
    
  • 検証済みのドメインを選択

    • DL_SRC
    • DL_DST
    • DL_TYPE
    • NW_SRC
    • NW_DST
    • IN_PORT
  • 検証コード

    import requests
    import json
    
    def add_flow(dpid, src_ip, dst_ip, in_port, out_port, src_mac,dst_mac,priority=10):
        flow = {
          
          
            "dpid": dpid,
            "idle_timeout": 0,
            "hard_timeout": 0,
            "priority": priority,
            "match":{
          
          
                "dl_type": 2048,
                "in_port": in_port,
                "nw_src": src_ip,
                "nw_dst": dst_ip,
                "dl_src":src_mac,
                "dl_dst":dst_mac
            },
            "actions":[
                {
          
          
                    "type":"OUTPUT",
                    "port": out_port
                }
            ]
        }
    
        url = 'http://localhost:8080/stats/flowentry/add'
        ret = requests.post(
            url, headers={
          
          'Accept': 'application/json'}, data=json.dumps(flow))
        print(ret)
    
    def show_path(src, dst, port_path):
        print('install mywaypoint path: {} -> {}'.format(src, dst))
        path = str(src) + ' -> '
        for node in port_path:
            path += '{}:s{}:{}'.format(*node) + ' -> '
        path += str(dst)
        path += '\n'
        print(path)
    
    def install_path():
        '23 -> 4:s22:2 -> 2:s9:3 -> 3:s16:2 -> 3:s7:2 -> 3:25:2 -> 1'
        src_sw, dst_sw = 23, 1
        waypoint_sw = 9  # Tinker 10.0.0.21, s9
    
        path = [(4, 22, 2), (2, 9, 3), (3, 16, 2), (3, 7, 2), (3, 25, 2)]
        # path = [(3, 7 , 2)]
        MIT_mac="00:00:00:00:00:01"
        SDC_mac="00:00:00:00:00:02"
        # send flow mod
        for node in path:
            in_port, dpid, out_port = node
            add_flow(dpid, '10.0.0.0/8', '10.0.0.0/8', in_port, out_port,SDC_mac,MIT_mac)
            add_flow(dpid, '10.0.0.0/8', '10.0.0.0/8', out_port, in_port,MIT_mac,SDC_mac)
        show_path(src_sw, dst_sw, path)
    
    if __name__ == '__main__':
        install_path()
    
  • 結果

  • 分析する

    • マッチングルール:

      ルール1 ルール2
      src_ip 10.0.0.0/8 10.0.0.0/8
      dst_ip 10.0.0.0/8 10.0.0.0/8
      src_mac 00:00:00:00:00:01 00:00:00:00:00:02
      dst_mac 00:00:00:00:00:02 00:00:00:00:00:01
      入力ポート 配信パス上の入力ポート 配信パス上の入力ポート
      dl_type 2048年 2048年
    • ECの数は予想通りベース部分の数と同じ3つです

    • 予想どおり、転送ループ 20.0.0.5–>20.0.0.7–>20.0.0.16–>20.0.09–>20.0.0.22–>20.0.0.23–>20.0.0.25 が存在します。

3 問題の並べ替え

  • 基本的な実験部分はパッチ適用失敗の問題など環境構成の問題のみであり、ソースコードの修正はパッチを適用したコード上で修正する必要があります。

  • ドメイン照合を実行すると、src_mac は正常に照合できますが、dst_mac は照合できません

    src_mac は送信元ホストの MAC アドレス、dst_mac は宛先ホストの MAC アドレスです。

    エラーの理由: 一致するdl_dstドメイン

おすすめ

転載: blog.csdn.net/qq_49588762/article/details/117699068