[CORD-2635][CORD-2632] Handle Clear Deferred actions. Support for DHCP packets trace. Output optimization to handle also gateways
Change-Id: Ief37895b8b80ecb10274deac506c3d0c8c127e6d
diff --git a/apps/t3/src/main/java/org/onosproject/t3/cli/TroubleshootTraceCommand.java b/apps/t3/src/main/java/org/onosproject/t3/cli/TroubleshootTraceCommand.java
index bdd67d1..7387c5f 100644
--- a/apps/t3/src/main/java/org/onosproject/t3/cli/TroubleshootTraceCommand.java
+++ b/apps/t3/src/main/java/org/onosproject/t3/cli/TroubleshootTraceCommand.java
@@ -88,9 +88,18 @@
@Option(name = "-ml", aliases = "--mplsLabel", description = "Mpls label of incoming packet")
String mplsLabel = null;
- @Option(name = "-mb", aliases = "--mplsBos", description = "MPLS BOS", valueToShowInHelp = "True")
+ @Option(name = "-mb", aliases = "--mplsBos", description = "MPLS BOS")
String mplsBos = null;
+ @Option(name = "-ipp", aliases = "--ipProto", description = "IP Proto")
+ String ipProto = null;
+
+ @Option(name = "-udps", aliases = "--udpSrc", description = "UDP Source")
+ String udpSrc = null;
+
+ @Option(name = "-udpd", aliases = "--udpDst", description = "UDP Destination")
+ String udpDst = null;
+
@Override
protected void execute() {
TroubleshootService service = get(TroubleshootService.class);
@@ -146,6 +155,19 @@
selectorBuilder.matchMplsBos(Boolean.valueOf(mplsBos));
}
+ if (ipProto != null) {
+ selectorBuilder.matchIPProtocol(Byte.valueOf(ipProto));
+ }
+
+ if (udpSrc != null) {
+ selectorBuilder.matchUdpSrc(TpPort.tpPort(Integer.parseInt(udpSrc)));
+ }
+
+ if (udpDst != null) {
+ selectorBuilder.matchUdpDst(TpPort.tpPort(Integer.parseInt(udpDst)));
+ }
+
+
TrafficSelector packet = selectorBuilder.build();
//Printing the created packet
diff --git a/apps/t3/src/main/java/org/onosproject/t3/impl/TroubleshootManager.java b/apps/t3/src/main/java/org/onosproject/t3/impl/TroubleshootManager.java
index 8f03ddc..8aeb874 100644
--- a/apps/t3/src/main/java/org/onosproject/t3/impl/TroubleshootManager.java
+++ b/apps/t3/src/main/java/org/onosproject/t3/impl/TroubleshootManager.java
@@ -70,6 +70,7 @@
import java.util.stream.Collectors;
import static org.onlab.packet.EthType.EtherType;
+import static org.onosproject.net.flow.TrafficSelector.*;
import static org.onosproject.net.flow.instructions.Instructions.GroupInstruction;
import static org.slf4j.LoggerFactory.getLogger;
@@ -168,23 +169,13 @@
computePath(completePath, trace, outputPath.getOutput());
break;
} else if (cp.port().equals(PortNumber.CONTROLLER)) {
+
//Getting the master when the packet gets sent as packet in
NodeId master = mastershipService.getMasterFor(cp.deviceId());
trace.addResultMessage("Packet goes to the controller " + master.id());
computePath(completePath, trace, outputPath.getOutput());
- } else if (outputPath.getFinalPacket().getCriterion(Criterion.Type.ETH_TYPE) != null &&
- ((EthTypeCriterion) outputPath.getFinalPacket().getCriterion(Criterion.Type.ETH_TYPE)).ethType()
- .equals(EtherType.ARP.ethType()) && deviceService.getPort(cp).isEnabled() &&
- linkService.getEgressLinks(cp).isEmpty()) {
- if (hostsList.isEmpty()) {
- trace.addResultMessage("Packet is ARP and reached " + cp + " with no hosts connected ");
- } else {
- trace.addResultMessage("Packet is ARP and reached " + cp + " with hosts " + hostsList);
- }
- computePath(completePath, trace, outputPath.getOutput());
-
- } else {
+ } else if (linkService.getEgressLinks(cp).size() > 0) {
//TODO this can be optimized if we use a Tree structure for paths.
//if we already have outputs let's check if the one we are considering starts from one of the devices
@@ -214,17 +205,11 @@
//let's compute the links for the given output
Set<Link> links = linkService.getEgressLinks(cp);
log.debug("Egress Links {}", links);
- //No links means that the packet gets dropped.
- if (links.size() == 0) {
- log.warn("No links out of {}", cp);
- computePath(completePath, trace, cp);
- trace.addResultMessage("No links depart from " + cp + ". Packet is dropped");
- }
//For each link we trace the corresponding device
for (Link link : links) {
ConnectPoint dst = link.dst();
//change in-port to the dst link in port
- TrafficSelector.Builder updatedPacket = DefaultTrafficSelector.builder();
+ Builder updatedPacket = DefaultTrafficSelector.builder();
outputPath.getFinalPacket().criteria().forEach(updatedPacket::add);
updatedPacket.add(Criteria.matchInPort(dst.port()));
log.debug("DST Connect Point {}", dst);
@@ -234,6 +219,23 @@
getTrace(completePath, dst, trace);
}
+ } else if (deviceService.getPort(cp).isEnabled()) {
+ if (hostsList.isEmpty()) {
+ trace.addResultMessage("Packet is " + ((EthTypeCriterion) outputPath.getFinalPacket()
+ .getCriterion(Criterion.Type.ETH_TYPE)).ethType() + " and reached " +
+ cp + " with no hosts connected ");
+ } else {
+ trace.addResultMessage("Packet is " + ((EthTypeCriterion) outputPath.getFinalPacket()
+ .getCriterion(Criterion.Type.ETH_TYPE)).ethType() + " and reached " +
+ cp + " with hosts " + hostsList);
+ }
+ computePath(completePath, trace, outputPath.getOutput());
+
+ } else {
+ //No links means that the packet gets dropped.
+ log.warn("No links out of {}", cp);
+ computePath(completePath, trace, cp);
+ trace.addResultMessage("No links depart from " + cp + ". Packet is dropped");
}
}
return trace;
@@ -327,6 +329,8 @@
List<FlowEntry> flows = new ArrayList<>();
List<FlowEntry> outputFlows = new ArrayList<>();
+ List<Instruction> deferredInstructions = new ArrayList<>();
+
FlowEntry nextTableIdEntry = findNextTableIdEntry(in.deviceId(), -1);
if (nextTableIdEntry == null) {
trace.addResultMessage("No flow rules for device " + in.deviceId() + ". Aborting");
@@ -396,14 +400,24 @@
outputFlows.add(flowEntry);
output = true;
}
- //update the packet according to the actions of this flow rule.
- packet = updatePacket(packet, flowEntry.treatment().allInstructions()).build();
+ //update the packet according to the immediate actions of this flow rule.
+ packet = updatePacket(packet, flowEntry.treatment().immediate()).build();
+
+ //save the deferred rules for later
+ deferredInstructions.addAll(flowEntry.treatment().deferred());
+
+ //If the flow requires to clear deferred actions we do so for all the ones we encountered.
+ if (flowEntry.treatment().clearedDeferred()) {
+ deferredInstructions.clear();
+ }
+
}
}
//Creating a modifiable builder for the output packet
- TrafficSelector.Builder builder = DefaultTrafficSelector.builder();
+ Builder builder = DefaultTrafficSelector.builder();
packet.criteria().forEach(builder::add);
+
//Adding all the flows to the trace
trace.addFlowsForDevice(in.deviceId(), ImmutableList.copyOf(flows));
@@ -449,10 +463,17 @@
Collection<FlowEntry> nonOutputFlows = flows;
nonOutputFlows.removeAll(outputFlowEntries);
+ //Handling groups pointed at by immediate instructions
for (FlowEntry entry : flows) {
- getGroupsFromInstructions(trace, groups, entry.treatment().allInstructions(),
+ getGroupsFromInstructions(trace, groups, entry.treatment().immediate(),
entry.deviceId(), builder, outputPorts, in);
}
+
+ //If we have deferred instructions at this point we handle them.
+ if (deferredInstructions.size() > 0) {
+ builder = handleDeferredActions(trace, packet, in, deferredInstructions, outputPorts, groups);
+
+ }
packet = builder.build();
log.debug("Groups hit by packet {}", packet);
groups.forEach(group -> {
@@ -463,10 +484,11 @@
outputPorts.forEach(port -> {
log.debug("Port {}", port);
});
- log.debug("Output Packet {}", packet);
+ log.info("Output Packet {}", packet);
return trace;
}
+
/**
* Handles table 27 in Ofpda which is a fixed table not visible to any controller that handles Mpls Labels.
*
@@ -507,6 +529,36 @@
.stream().filter(f -> ((IndexTableId) f.table()).id() > currentId).min(comparator).orElse(null);
}
+ private Builder handleDeferredActions(StaticPacketTrace trace, TrafficSelector packet,
+ ConnectPoint in, List<Instruction> deferredInstructions,
+ List<PortNumber> outputPorts, List<Group> groups) {
+
+ //Update the packet with the deferred instructions
+ Builder builder = updatePacket(packet, deferredInstructions);
+
+ //Gather any output instructions from the deferred instruction
+ List<Instruction> outputFlowInstruction = deferredInstructions.stream().filter(instruction -> {
+ return instruction.type().equals(Instruction.Type.OUTPUT);
+ }).collect(Collectors.toList());
+
+ //We are considering deferred instructions from flows, there can only be one output.
+ if (outputFlowInstruction.size() > 1) {
+ trace.addResultMessage("More than one flow rule with OUTPUT instruction");
+ log.warn("There cannot be more than one flow entry with OUTPUT instruction for {}", packet);
+ }
+ //If there is one output let's go through that
+ if (outputFlowInstruction.size() == 1) {
+ buildOutputFromDevice(trace, in, builder, outputPorts, (OutputInstruction) outputFlowInstruction.get(0),
+ ImmutableList.of());
+ }
+ //If there is no output let's see if there any deferred instruction point to groups.
+ if (outputFlowInstruction.size() == 0) {
+ getGroupsFromInstructions(trace, groups, deferredInstructions,
+ in.deviceId(), builder, outputPorts, in);
+ }
+ return builder;
+ }
+
/**
* Gets group information from instructions.
*
@@ -519,7 +571,7 @@
*/
private void getGroupsFromInstructions(StaticPacketTrace trace, List<Group> groupsForDevice,
List<Instruction> instructions, DeviceId deviceId,
- TrafficSelector.Builder builder, List<PortNumber> outputPorts,
+ Builder builder, List<PortNumber> outputPorts,
ConnectPoint in) {
List<Instruction> groupInstructionlist = new ArrayList<>();
for (Instruction instruction : instructions) {
@@ -569,9 +621,9 @@
* @param builder the packet builder
* @param outputPorts the list of output ports for this device
* @param outputInstruction the output instruction
- * @param groupsForDevice
+ * @param groupsForDevice the groups we output from
*/
- private void buildOutputFromDevice(StaticPacketTrace trace, ConnectPoint in, TrafficSelector.Builder builder,
+ private void buildOutputFromDevice(StaticPacketTrace trace, ConnectPoint in, Builder builder,
List<PortNumber> outputPorts, OutputInstruction outputInstruction,
List<Group> groupsForDevice) {
ConnectPoint output = new ConnectPoint(in.deviceId(), outputInstruction.port());
@@ -592,8 +644,8 @@
* @param instructions the set of instructions
* @return the packet with the applied instructions
*/
- private TrafficSelector.Builder updatePacket(TrafficSelector packet, List<Instruction> instructions) {
- TrafficSelector.Builder newSelector = DefaultTrafficSelector.builder();
+ private Builder updatePacket(TrafficSelector packet, List<Instruction> instructions) {
+ Builder newSelector = DefaultTrafficSelector.builder();
packet.criteria().forEach(newSelector::add);
//FIXME optimize
for (Instruction instruction : instructions) {
@@ -609,7 +661,7 @@
* @param instruction the instruction to be translated
* @return the new selector with the applied instruction
*/
- private TrafficSelector.Builder translateInstruction(TrafficSelector.Builder newSelector, Instruction instruction) {
+ private Builder translateInstruction(Builder newSelector, Instruction instruction) {
log.debug("Translating instruction {}", instruction);
log.debug("New Selector {}", newSelector.build());
//TODO add as required
@@ -640,7 +692,7 @@
//When popping MPLS we remove label and BOS
TrafficSelector temporaryPacket = newSelector.build();
if (temporaryPacket.getCriterion(Criterion.Type.MPLS_LABEL) != null) {
- TrafficSelector.Builder noMplsSelector = DefaultTrafficSelector.builder();
+ Builder noMplsSelector = DefaultTrafficSelector.builder();
temporaryPacket.criteria().stream().filter(c -> {
return !c.type().equals(Criterion.Type.MPLS_LABEL) &&
!c.type().equals(Criterion.Type.MPLS_BOS);
diff --git a/apps/t3/src/test/java/org/onosproject/t3/impl/T3TestObjects.java b/apps/t3/src/test/java/org/onosproject/t3/impl/T3TestObjects.java
index 5f6a7d9..9009c8e 100644
--- a/apps/t3/src/test/java/org/onosproject/t3/impl/T3TestObjects.java
+++ b/apps/t3/src/test/java/org/onosproject/t3/impl/T3TestObjects.java
@@ -440,6 +440,44 @@
static final Group DUAL_LINK_GROUP = new DefaultGroup(GROUP_ID, DUAL_LINK_1, Group.Type.SELECT, BUCKETS_DUAL);
+ //Clear Deferred
+ static final DeviceId DEFERRED_1 = DeviceId.deviceId("Deferred");
+
+ static final ConnectPoint DEFERRED_CP_1_IN = ConnectPoint.deviceConnectPoint(DEFERRED_1 + "/" + 1);
+ static final ConnectPoint DEFERRED_CP_2_OUT = ConnectPoint.deviceConnectPoint(DEFERRED_1 + "/" + 2);
+
+ //match on port 1 and apply deferred actions
+ private static final TrafficTreatment DEFERRED_1_FLOW_TREATMENT = DefaultTrafficTreatment.builder()
+ .transition(10)
+ .deferred()
+ .pushMpls()
+ .setMpls(MplsLabel.mplsLabel(100))
+ .build();
+ private static final FlowRule DEFERRED_FLOW = DefaultFlowEntry.builder().forDevice(DEFERRED_1)
+ .forTable(0)
+ .withPriority(100)
+ .withSelector(SINGLE_FLOW_SELECTOR)
+ .withTreatment(DEFERRED_1_FLOW_TREATMENT)
+ .fromApp(new DefaultApplicationId(0, "TestApp"))
+ .makePermanent()
+ .build();
+ static final FlowEntry DEFERRED_FLOW_ENTRY = new DefaultFlowEntry(DEFERRED_FLOW);
+
+ //match on port 1, clear deferred actions and output
+ private static final TrafficTreatment DEFERRED_CLEAR_1_FLOW_TREATMENT = DefaultTrafficTreatment.builder()
+ .wipeDeferred()
+ .setOutput(PortNumber.portNumber(2))
+ .build();
+ private static final FlowRule DEFERRED_CLEAR_FLOW = DefaultFlowEntry.builder().forDevice(DEFERRED_1)
+ .forTable(10)
+ .withPriority(100)
+ .withSelector(SINGLE_FLOW_SELECTOR)
+ .withTreatment(DEFERRED_CLEAR_1_FLOW_TREATMENT)
+ .fromApp(new DefaultApplicationId(0, "TestApp"))
+ .makePermanent()
+ .build();
+ static final FlowEntry DEFERRED_CLEAR_FLOW_ENTRY = new DefaultFlowEntry(DEFERRED_CLEAR_FLOW);
+
//helper elements
static final String MASTER_1 = "Master1";
diff --git a/apps/t3/src/test/java/org/onosproject/t3/impl/TroubleshootManagerTest.java b/apps/t3/src/test/java/org/onosproject/t3/impl/TroubleshootManagerTest.java
index d7d3f33..7c584c9 100644
--- a/apps/t3/src/test/java/org/onosproject/t3/impl/TroubleshootManagerTest.java
+++ b/apps/t3/src/test/java/org/onosproject/t3/impl/TroubleshootManagerTest.java
@@ -272,6 +272,21 @@
}
+ /**
+ * Test proper clear deferred behaviour.
+ */
+ @Test
+ public void clearDeferred() throws Exception {
+
+ StaticPacketTrace traceSuccess = testSuccess(PACKET_OK, DEFERRED_CP_1_IN,
+ DEFERRED_1, DEFERRED_CP_2_OUT, 1, 1);
+
+ assertNull("MPLS should have been not applied due to clear deferred", traceSuccess
+ .getGroupOuputs(DEFERRED_1).get(0).getFinalPacket().getCriterion(Criterion.Type.MPLS_LABEL));
+
+ }
+
+
private StaticPacketTrace testSuccess(TrafficSelector packet, ConnectPoint in, DeviceId deviceId, ConnectPoint out,
int paths, int outputs) {
StaticPacketTrace traceSuccess = mngr.trace(packet, in);
@@ -329,6 +344,8 @@
return ImmutableList.of(DUAL_LINK_1_GROUP_FLOW_ENTRY, DUAL_LINK_2_GROUP_FLOW_ENTRY);
} else if (deviceId.equals(DUAL_LINK_3)) {
return ImmutableList.of(DUAL_LINK_3_FLOW_ENTRY, DUAL_LINK_3_FLOW_ENTRY_2);
+ } else if (deviceId.equals(DEFERRED_1)) {
+ return ImmutableList.of(DEFERRED_FLOW_ENTRY, DEFERRED_CLEAR_FLOW_ENTRY);
}
return ImmutableList.of();
}