aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/onos/apps/flowanalyzer/src/main/java/org/onosproject/flowanalyzer/FlowAnalyzer.java
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/onos/apps/flowanalyzer/src/main/java/org/onosproject/flowanalyzer/FlowAnalyzer.java')
-rw-r--r--framework/src/onos/apps/flowanalyzer/src/main/java/org/onosproject/flowanalyzer/FlowAnalyzer.java213
1 files changed, 206 insertions, 7 deletions
diff --git a/framework/src/onos/apps/flowanalyzer/src/main/java/org/onosproject/flowanalyzer/FlowAnalyzer.java b/framework/src/onos/apps/flowanalyzer/src/main/java/org/onosproject/flowanalyzer/FlowAnalyzer.java
index 5d99d746..6aaaaee8 100644
--- a/framework/src/onos/apps/flowanalyzer/src/main/java/org/onosproject/flowanalyzer/FlowAnalyzer.java
+++ b/framework/src/onos/apps/flowanalyzer/src/main/java/org/onosproject/flowanalyzer/FlowAnalyzer.java
@@ -21,12 +21,31 @@ import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.FlowEntry;
import org.onosproject.net.flow.FlowRuleService;
-import org.onosproject.net.host.HostService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.HostId;
+import org.onosproject.net.flow.criteria.Criteria;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.PortCriterion;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.topology.TopologyService;
+import org.onosproject.net.topology.TopologyGraph;
import org.onosproject.net.link.LinkService;
+import org.onosproject.net.Link;
+import org.onosproject.net.topology.TopologyVertex;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
+import java.util.HashSet;
+import java.util.List;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
import static org.slf4j.LoggerFactory.getLogger;
/**
@@ -42,11 +61,10 @@ public class FlowAnalyzer {
protected FlowRuleService flowRuleService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected LinkService linkService;
+ protected TopologyService topologyService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected HostService hostService;
-
+ protected LinkService linkService;
@Activate
public void activate(ComponentContext context) {
@@ -58,12 +76,193 @@ public class FlowAnalyzer {
log.info("Stopped");
}
+ TopologyGraph graph;
+ Map<FlowEntry, String> label = new HashMap<>();
+ Set<FlowEntry> ignoredFlows = new HashSet<>();
/**
- * ...
+ * Analyzes and prints out a report on the status of every flow entry inside
+ * the network. The possible states are: Cleared (implying that the entry leads to
+ * a host), Cycle (implying that it is part of cycle), and Black Hole (implying
+ * that the entry does not lead to a single host).
*/
- public void analyze() {
- // TODO: implement this
+ public String analyze() {
+ graph = topologyService.getGraph(topologyService.currentTopology());
+ for (TopologyVertex v: graph.getVertexes()) {
+ DeviceId srcDevice = v.deviceId();
+ Iterable<FlowEntry> flowTable = flowRuleService.getFlowEntries(srcDevice);
+ for (FlowEntry flow: flowTable) {
+ dfs(flow);
+ }
+ }
+
+ //analyze the cycles to look for "critical flows" that can be removed
+ //to break the cycle
+ Set<FlowEntry> critpts = new HashSet<>();
+ for (FlowEntry flow: label.keySet()) {
+ if ("Cycle".equals(label.get(flow))) {
+ Map<FlowEntry, String> labelSaved = label;
+ label = new HashMap<FlowEntry, String>();
+ ignoredFlows.add(flow);
+ for (TopologyVertex v: graph.getVertexes()) {
+ DeviceId srcDevice = v.deviceId();
+ Iterable<FlowEntry> flowTable = flowRuleService.getFlowEntries(srcDevice);
+ for (FlowEntry flow1: flowTable) {
+ dfs(flow1);
+ }
+ }
+
+ boolean replacable = true;
+ for (FlowEntry flow2: label.keySet()) {
+ if ("Cleared".equals(labelSaved.get(flow2)) && !("Cleared".equals(label.get(flow2)))) {
+ replacable = false;
+ }
+ }
+ if (replacable) {
+ critpts.add(flow);
+ }
+ label = labelSaved;
+ }
+ }
+
+ for (FlowEntry flow: critpts) {
+ label.put(flow, "Cycle Critical Point");
+ }
+
+ String s = "\n";
+ for (FlowEntry flow: label.keySet()) {
+ s += ("Flow Rule: " + flowEntryRepresentation(flow) + "\n");
+ s += ("Analysis: " + label.get(flow) + "!\n\n");
+ }
+ s += ("Analyzed " + label.keySet().size() + " flows.");
+ //log.info(s);
+ return s;
+ }
+
+ public Map<FlowEntry, String> calcLabels() {
+ analyze();
+ return label;
+ }
+ public String analysisOutput() {
+ analyze();
+ String s = "\n";
+ for (FlowEntry flow: label.keySet()) {
+ s += ("Flow Rule: " + flowEntryRepresentation(flow) + "\n");
+ s += ("Analysis: " + label.get(flow) + "!\n\n");
+ }
+ return s;
+ }
+
+ private boolean dfs(FlowEntry flow) {
+ if (ignoredFlows.contains(flow)) {
+ return false;
+ }
+ if ("Cycle".equals(label.get(flow)) ||
+ "Black Hole".equals(label.get(flow)) ||
+ "Cleared".equals(label.get(flow)) ||
+ "NA".equals(label.get(flow)) ||
+ "Cycle Critical Point".equals(label.get(flow))) {
+
+ // This flow has already been analyzed and there is no need to analyze it further
+ return !"Black Hole".equals(label.get(flow));
+ }
+
+ if ("Visiting".equals(label.get(flow))) {
+ //you've detected a cycle because you reached the same entry again during your dfs
+ //let it continue so you can label the whole cycle
+ label.put(flow, "Cycle");
+ } else {
+ //otherwise, mark off the current flow entry as currently being visited
+ label.put(flow, "Visiting");
+ }
+
+ boolean pointsToLiveEntry = false;
+
+ List<Instruction> instructions = flow.treatment().allInstructions();
+ for (Instruction i: instructions) {
+ if (i instanceof Instructions.OutputInstruction) {
+ pointsToLiveEntry |= analyzeInstruction(i, flow);
+ }
+ if ("NA".equals(label.get(flow))) {
+ return pointsToLiveEntry;
+ }
+ }
+
+ if (!pointsToLiveEntry) {
+ //this entry does not point to any "live" entries thus must be a black hole
+ label.put(flow, "Black Hole");
+ } else if ("Visiting".equals(label.get(flow))) {
+ //the flow is not in a cycle or in a black hole
+ label.put(flow, "Cleared");
+ }
+ return pointsToLiveEntry;
}
+ private boolean analyzeInstruction(Instruction i, FlowEntry flow) {
+ boolean pointsToLiveEntry = false;
+ Instructions.OutputInstruction output = (Instructions.OutputInstruction) i;
+ PortNumber port = output.port();
+ PortNumber outPort = null;
+
+ DeviceId egress = null;
+ boolean hasHost = false;
+
+ ConnectPoint portPt = new ConnectPoint(flow.deviceId(), port);
+ for (Link l: linkService.getEgressLinks(portPt)) {
+ if (l.dst().elementId() instanceof DeviceId) {
+ egress = l.dst().deviceId();
+ outPort = l.dst().port();
+ } else if (l.dst().elementId() instanceof HostId) {
+ //the port leads to a host: therefore it is not a dead link
+ pointsToLiveEntry = true;
+ hasHost = true;
+ }
+ }
+ if (!topologyService.isInfrastructure(topologyService.currentTopology(), portPt) && egress == null) {
+ pointsToLiveEntry = true;
+ hasHost = true;
+ }
+ if (hasHost) {
+ return pointsToLiveEntry;
+ }
+ if (egress == null) {
+ //the port that the flow instructions tells you to send the packet
+ //to doesn't exist or is a controller port
+ label.put(flow, "NA");
+ return pointsToLiveEntry;
+ }
+
+ Iterable<FlowEntry> dstFlowTable = flowRuleService.getFlowEntries(egress);
+
+ Set<Criterion> flowCriteria = flow.selector().criteria();
+
+ //filter the criteria in order to remove port dependency
+ Set<Criterion> filteredCriteria = new HashSet<>();
+ for (Criterion criterion : flowCriteria) {
+ if (!(criterion instanceof PortCriterion)) {
+ filteredCriteria.add(criterion);
+ }
+ }
+
+ //ensure that the in port is equal to the port that it is coming in from
+ filteredCriteria.add(Criteria.matchInPort(outPort));
+
+ for (FlowEntry entry: dstFlowTable) {
+ if (ignoredFlows.contains(entry)) {
+ continue;
+ }
+ if (filteredCriteria.containsAll(entry.selector().criteria())) {
+ dfs(entry);
+
+ if (!"Black Hole".equals(label.get(entry))) {
+ //this entry is "live" i.e not a black hole
+ pointsToLiveEntry = true;
+ }
+ }
+ }
+ return pointsToLiveEntry;
+ }
+ public String flowEntryRepresentation(FlowEntry flow) {
+ return "Device: " + flow.deviceId() + ", " + flow.selector().criteria() + ", " + flow.treatment().immediate();
+ }
}