SDN Experiment4

1 Experiment topic

Suppose you are a network administrator who lived in 1972 and maintained ARPAnet. In the previous experiment, you learned how to establish the shortest path, and issued a path with the least number of hops from SDC to MIT (the green path in the figure). One day, your colleague Bob received a new requirement that all traffic between UTAH and ILLINOIS must pass through the traffic analyzer deployed in TINKER for further research. The careless Bob quickly went down without checking the current network status. Send a new path (red path in the picture). Smart and witty, you quickly realize that the flow table issued by Bob may cause a forwarding loop.

You are now required to run the VeriFlow tool, check the above two forwarding paths, and complete the following two parts of the experiment:

  • Basic experiment part
  1. Output the number of ECs affected each time
  2. Print out information about the loop path
  3. Further print out the relevant information of the EC corresponding to the loop
  4. Analyze the difference between the original code and the patch code, and think about why a patch needs to be added
  • Expand the experimental part
  1. If you modify waypoint_path.pythe priority field of the added rule in the code, the detection result of VeriFlow will be wrong. Please describe what the error is and explain the reason for the error
  2. Among the 14 domains supported by VeriFlow, select multiple domains (not less than 5) for verification, output and analyze the results
  • Experiment data download
    https://www.aliyundrive.com/s/iA9A9BWijz7

2 Experimental content

2.1 preparation

2.1.1 Observe the forwarding loop problem

  • Start the shortest path control program
    ryu-manager ofctl_rest.py shortest_path.py --observe-links

  • start topology

    sudo python Arpanet19723.py

  • In the topology, SDC pings MIT to establish a connection

    SDC ping MIT

  • Issue the route from UTAH to ILLINOIS via TINKER

    sudo python waypoint_path.py

  • In the topology, SDC pings MIT again to establish a connection

    SDC ping MIT

  • View a certain switch on the path, such as the flow table of USC

    sudo ovs-ofctl dump-flows s22

  • Open wireshark to observe the port

2.1.2 Using VeriFlow

  • Download VeriFlow from github and apply the experimental patch

    git clone https://github.com/samueljero/BEADS.git 
    cd BEADS 
    git am 0001-for-xjtu-sdn-exp-2020.patch 
    
  • Compile VeriFlow

    cd veriflow/VeriFlow 
    make clean all 
    
  • Open the remote controller on the custom port and run the shortest program

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

  • Run VeriFlow in proxy mode

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

  • start topology

    sudo python Arpanet19723.py

  • In the topology, SDC pings MIT to establish a connection

    SDC ping MIT

  • Issue the path from UTAH to ILLINOIS via TINKER, and observe the loop information detected by VeriFlow in the log file

    sudo python waypoint_path.py

2.2 Basic experiment part

2.2.1 Printing of EC number

  • VeriFlow::verifyRule()It is a function to execute the VeriFlow core algorithm, including the division of equivalence classes, the construction of forwarding graphs and the verification of invariants. The variable in the function ecCountis the number of ECs.

  • It is only necessary to ecCountprint out to a log file.

  • VeriFlow::verifyRule()before fixing:

    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()After modification:

    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);//输出到日志文件
    }
    
  • Recompile, log file screenshot

2.2.2 Printing of the loop path

  • VeriFlow::traverseForwardingGraph()To traverse the forwarding graph of a particular EC, verify whether there are loops or black holes. It is only necessary to add a variable vector<string> loop_pathto record the loop path.

  • VeriFlow::traverseForwardingGraph()After modification:

    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);
    }
    
  • Recompile, log file screenshot

2.2.3 Printing of related data package information

  • The basic information of EC is displayed in the interval format of 14 domains. To facilitate Bob’s troubleshooting, the representation of EC information is simplified, and only the TCP/IP quintuple is extracted from the 14 domains as the main information display.

  • Basic Information Print Code of Existing EC

    VeriFlow::traverseForwardingGraph():

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

  • Imitate EquivalenceClass::toString()a function, EquivalenceClassadd TcpIptoString()a function to it

    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;
    }
    
  • Recompile, log file screenshot

2.2.4 Analyze the difference between the original code and the patch code, and think about why it is necessary to add a patch

  • change file

    • veriflow/VeriFlow/Network.cpp | 1 +

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

    • veriflow/VeriFlow/Rule.cpp | 8 ++±

    • veriflow/VeriFlow/Rule.h | 1 +

    • veriflow/VeriFlow/VeriFlow.cpp | 45 +++++++++++++++±–

    • veriflow/VeriFlow/VeriFlow.h | 2 ±

  • View patch modification content and analysis

    • use command

      git diff HEAD origin/HEAD

    • Added in_portattributes to Rule, added in_portstorage and processing of pairs

    • Store lastHop, improved ability to find BlackHole

    • Improve the situation of judging black holes

      In the open source code in github, two situations for judging black holes are given:

      • The current switch or host is not in the current network
      • The current switch or host is in the network, but there is no link to other switches or hosts

      A case of judging black holes has been added in the patch:

      • The current switch or host also has a link connected to it in the topology of the network, but due to changes in the network structure, the previous hop cannot be found from the location of the current switch or host and the corresponding port (in_port). switch or host.

    • Improve the judgment of the loop

      Selecting the next hop avoids the same as the previous hop

2.3 Expand the experimental part

2.3.1 If you modify waypoint_path.pythe priority field of the added rule in the code, the detection result of VeriFlow will be wrong. Try to describe what the error is and explain the reason for the error

  • Modify waypoint_path.pythe priority field of the added rule in the code to 1, and find that there is no loop in the log file, and it cannot SDC ping MITbe blocked

  • watch flow table

    • Before modifying the priority field

      • s22 flow table

      • s25 flow table

    • After modifying the priority field

      • s22 flow table

      • s25 flow table

    • When the matching fields are the same, the new flow entry covers the old flow entry. In fact, there is a loop, but VeriFlow does not detect the loop.

  • Unable to find the cause of the loop

    • When judging the loop to select the next hop, veriflow uses the priority field to sort. When there are rules with the same priority, problems will occur.

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

    • When a rule completely matches and needs to overwrite the original rule, VeriFlow does not delete the original rule and add a new rule, but retains the original rule and discards the newly added rule.

2.3.2 Among the 14 domains supported by VeriFlow, select multiple domains (not less than 5) for verification, output and analyze the results

  • Domains that can be verified

    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
    };
    
  • Select verified domain

    • DL_SRC
    • DL_DST
    • DL_TYPE
    • NW_SRC
    • NW_DST
    • IN_PORT
  • verification code

    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()
    
  • result

  • analyze

    • Matching rules:

      Rule 1 Rule 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
      in_port The ingress port on the delivery path path The ingress port on the delivery path path
      dl_type 2048 2048
    • The number of ECs is 3, which is the same as the number of the base part, as expected

    • There will be a forwarding loop 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, as expected

3 problem sorting

  • The basic experiment part is only a problem of environment configuration, such as the problem of patching failure. Modifying the source code needs to be modified on the patched code.

  • When performing domain matching, src_mac can match successfully, but dst_mac does not match

    src_mac is the mac address of the source host; dst_mac is the mac address of the destination host

    Reason for error: wrong writing of matching dl_dstdomain name

Guess you like

Origin blog.csdn.net/qq_49588762/article/details/117699068