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
- Output the number of ECs affected each time
- Print out information about the loop path
- Further print out the relevant information of the EC corresponding to the loop
- 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
- If you modify
waypoint_path.py
the 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 - 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 functionecCount
is the number of ECs. -
It is only necessary to
ecCount
print 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 variablevector<string> loop_path
to 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,EquivalenceClass
addTcpIptoString()
a function to itstring 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_port
attributes to Rule, addedin_port
storage 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.py
the 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.py
the priority field of the added rule in the code to 1, and find that there is no loop in the log file, and it cannotSDC ping MIT
be 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_dst
domain name