T3 Handling lldp and Bddp
Change-Id: Iecbe22bbbaa6d4719092be97385f70828d628b8c
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 587c748..ae68d93 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
@@ -16,6 +16,7 @@
package org.onosproject.t3.cli;
+import com.google.common.base.Preconditions;
import org.apache.karaf.shell.commands.Command;
import org.apache.karaf.shell.commands.Option;
import org.onlab.packet.IpAddress;
@@ -25,6 +26,8 @@
import org.onlab.packet.VlanId;
import org.onosproject.cli.AbstractShellCommand;
import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
@@ -53,6 +56,8 @@
private static final String GROUP_BUCKET_FORMAT =
" id=0x%s, bucket=%s, bytes=%s, packets=%s, actions=%s";
+ private static final String CONTROLLER = "CONTROLLER";
+
@Option(name = "-v", aliases = "--verbose", description = "Outputs complete path")
private boolean verbosity1 = false;
@@ -80,7 +85,7 @@
@Option(name = "-dm", aliases = "--dstMac", description = "Destination MAC")
String dstMac = null;
- @Option(name = "-dtp", aliases = "dstTcpPort", description = "destination TCP Port")
+ @Option(name = "-dtp", aliases = "--dstTcpPort", description = "destination TCP Port")
String dstTcpPort = null;
@Option(name = "-vid", aliases = "--vlanId", description = "Vlan of incoming packet", valueToShowInHelp = "None")
@@ -104,7 +109,17 @@
@Override
protected void execute() {
TroubleshootService service = get(TroubleshootService.class);
- ConnectPoint cp = ConnectPoint.deviceConnectPoint(srcPort);
+ String[] cpInfo = srcPort.split("/");
+ Preconditions.checkArgument(cpInfo.length == 2, "wrong format of source port");
+ ConnectPoint cp;
+ //Uses input port as a convenience to carry the Controller port, proper flood behaviour is handled in the
+ // troubleshoot manager.
+ if (cpInfo[1].equalsIgnoreCase(CONTROLLER)) {
+ cp = new ConnectPoint(DeviceId.deviceId(cpInfo[0]), PortNumber.CONTROLLER);
+ } else {
+ cp = ConnectPoint.deviceConnectPoint(srcPort);
+ }
+
EtherType type = EtherType.valueOf(ethType.toUpperCase());
//Input Port must be specified
@@ -176,7 +191,6 @@
//Build the trace
StaticPacketTrace trace = service.trace(packet, cp);
-
//Print based on verbosity
if (verbosity1) {
printTrace(trace, false);
@@ -196,17 +210,27 @@
paths.forEach(path -> {
print("Path %s", path);
ConnectPoint previous = null;
- for (ConnectPoint connectPoint : path) {
- if (previous == null || !previous.deviceId().equals(connectPoint.deviceId())) {
- print("Device %s", connectPoint.deviceId());
- print("Input from %s", connectPoint);
- printFlows(trace, verbose, connectPoint);
- } else {
- printGroups(trace, verbose, connectPoint);
- print("Output through %s", connectPoint);
- print("");
+ if (path.size() == 1) {
+ ConnectPoint connectPoint = path.get(0);
+ print("Device %s", connectPoint.deviceId());
+ print("Input from %s", connectPoint);
+ printFlows(trace, verbose, connectPoint);
+ printGroups(trace, verbose, connectPoint);
+ print("Output through %s", connectPoint);
+ print("");
+ } else {
+ for (ConnectPoint connectPoint : path) {
+ if (previous == null || !previous.deviceId().equals(connectPoint.deviceId())) {
+ print("Device %s", connectPoint.deviceId());
+ print("Input from %s", connectPoint);
+ printFlows(trace, verbose, connectPoint);
+ } else {
+ printGroups(trace, verbose, connectPoint);
+ print("Output through %s", connectPoint);
+ print("");
+ }
+ previous = connectPoint;
}
- previous = connectPoint;
}
print("---------------------------------------------------------------\n");
});
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 bbec3ea..a1b1d68 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
@@ -30,6 +30,7 @@
import org.onosproject.net.DeviceId;
import org.onosproject.net.Host;
import org.onosproject.net.Link;
+import org.onosproject.net.Port;
import org.onosproject.net.PortNumber;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.driver.DriverService;
@@ -90,6 +91,8 @@
private static final Logger log = getLogger(TroubleshootManager.class);
+ static final String PACKET_TO_CONTROLLER = "Packet goes to the controller";
+
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected FlowRuleService flowRuleService;
@@ -177,7 +180,7 @@
//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());
+ trace.addResultMessage(PACKET_TO_CONTROLLER + " " + master.id());
computePath(completePath, trace, outputPath.getOutput());
handleVlanToController(outputPath, trace);
@@ -226,16 +229,22 @@
}
} 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);
+ EthTypeCriterion ethTypeCriterion = (EthTypeCriterion) trace.getInitialPacket()
+ .getCriterion(Criterion.Type.ETH_TYPE);
+ //We treat as correct output only if it's not LLDP or BDDP
+ if (!(ethTypeCriterion.ethType().equals(EtherType.LLDP.ethType())
+ || !ethTypeCriterion.ethType().equals(EtherType.BDDP.ethType()))) {
+ 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());
}
- computePath(completePath, trace, outputPath.getOutput());
} else {
//No links means that the packet gets dropped.
@@ -358,6 +367,17 @@
return trace;
}
+ //handle when the input is the controller
+ //NOTE, we are using the input port as a convenience to carry the CONTROLLER port number even if
+ // a packet in from the controller will not actually traverse the pipeline and have no such notion
+ // as the input port.
+ if (in.port().equals(PortNumber.CONTROLLER)) {
+ StaticPacketTrace outputTrace = inputFromController(trace, in);
+ if (outputTrace != null) {
+ return trace;
+ }
+ }
+
List<FlowEntry> flows = new ArrayList<>();
List<FlowEntry> outputFlows = new ArrayList<>();
@@ -532,6 +552,37 @@
return trace;
}
+ /**
+ * Handles the specific case where the Input is the controller.
+ * Note that the in port is used as a convenience to store the port of the controller even if the packet in
+ * from a controller should not have a physical input port. The in port from the Controller is used to make sure
+ * the flood to all active physical ports of the device.
+ *
+ * @param trace the trace
+ * @param in the controller port
+ * @return the augmented trace.
+ */
+ private StaticPacketTrace inputFromController(StaticPacketTrace trace, ConnectPoint in) {
+ EthTypeCriterion ethTypeCriterion = (EthTypeCriterion) trace.getInitialPacket()
+ .getCriterion(Criterion.Type.ETH_TYPE);
+ //If the packet is LLDP or BDDP we flood it on all active ports of the switch.
+ if (ethTypeCriterion != null && (ethTypeCriterion.ethType().equals(EtherType.LLDP.ethType())
+ || ethTypeCriterion.ethType().equals(EtherType.BDDP.ethType()))) {
+ //get the active ports
+ List<Port> enabledPorts = deviceService.getPorts(in.deviceId()).stream()
+ .filter(Port::isEnabled)
+ .collect(Collectors.toList());
+ //build an output from each one
+ enabledPorts.forEach(port -> {
+ GroupsInDevice output = new GroupsInDevice(new ConnectPoint(port.element().id(), port.number()),
+ ImmutableList.of(), trace.getInitialPacket());
+ trace.addGroupOutputPath(in.deviceId(), output);
+ });
+ return trace;
+ }
+ return null;
+ }
+
private boolean needsSecondTable10Flow(FlowEntry flowEntry, boolean isOfdpaHardware) {
return isOfdpaHardware && flowEntry.table().equals(IndexTableId.of(10))
&& flowEntry.selector().getCriterion(Criterion.Type.VLAN_VID) != null
@@ -713,6 +764,8 @@
List<PortNumber> outputPorts, OutputInstruction outputInstruction,
List<Group> groupsForDevice) {
ConnectPoint output = new ConnectPoint(in.deviceId(), outputInstruction.port());
+
+ //if the output is the input same we drop, except if the output is the controller
if (output.equals(in)) {
trace.addResultMessage("Connect point out " + output + " is same as initial input " +
trace.getInitialConnectPoint());
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 abbe77e..4e1eb68 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
@@ -539,6 +539,29 @@
.build();
static final FlowEntry DEFERRED_CLEAR_FLOW_ENTRY = new DefaultFlowEntry(DEFERRED_CLEAR_FLOW);
+ //LLDP
+
+ static final DeviceId LLDP_FLOW_DEVICE = DeviceId.deviceId("LldpDevice");
+
+ private static final TrafficSelector LLDP_FLOW_SELECTOR = DefaultTrafficSelector.builder()
+ .matchInPort(PortNumber.portNumber(1))
+ .matchEthType(EthType.EtherType.LLDP.ethType().toShort())
+ .build();
+
+ private static final TrafficTreatment LLDP_FLOW_TREATMENT = DefaultTrafficTreatment.builder()
+ .setOutput(PortNumber.CONTROLLER).build();
+ private static final FlowRule LLDP_FLOW = DefaultFlowEntry.builder().forDevice(LLDP_FLOW_DEVICE)
+ .forTable(0)
+ .withPriority(100)
+ .withSelector(LLDP_FLOW_SELECTOR)
+ .withTreatment(LLDP_FLOW_TREATMENT)
+ .fromApp(new DefaultApplicationId(0, "TestApp"))
+ .makePermanent()
+ .build();
+ static final FlowEntry LLDP_FLOW_ENTRY = new DefaultFlowEntry(LLDP_FLOW);
+
+ static final ConnectPoint LLDP_FLOW_CP = ConnectPoint.deviceConnectPoint(LLDP_FLOW_DEVICE + "/" + 1);
+
//helper elements
static final String MASTER_1 = "Master1";
@@ -571,6 +594,11 @@
.matchEthType(EthType.EtherType.ARP.ethType().toShort())
.build();
+ static final TrafficSelector PACKET_LLDP = DefaultTrafficSelector.builder()
+ .matchInPort(PortNumber.portNumber(1))
+ .matchEthType(EthType.EtherType.LLDP.ethType().toShort())
+ .build();
+
static final TrafficSelector PACKET_FAIL = DefaultTrafficSelector.builder()
.matchInPort(PortNumber.portNumber(1))
.matchIPSrc(IpPrefix.valueOf("127.0.0.1/32"))
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 9767730..7b6c570 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
@@ -62,6 +62,7 @@
import static org.junit.Assert.assertTrue;
import static org.onosproject.net.Device.Type.SWITCH;
import static org.onosproject.t3.impl.T3TestObjects.*;
+import static org.onosproject.t3.impl.TroubleshootManager.PACKET_TO_CONTROLLER;
import static org.slf4j.LoggerFactory.getLogger;
/**
@@ -132,7 +133,7 @@
StaticPacketTrace traceSuccess = mngr.trace(PACKET_ARP, ARP_FLOW_CP);
assertNotNull("Trace should not be null", traceSuccess);
assertTrue("Trace should be successful",
- traceSuccess.resultMessage().contains("Packet goes to the controller"));
+ traceSuccess.resultMessage().contains(PACKET_TO_CONTROLLER));
assertTrue("Master should be Master1",
traceSuccess.resultMessage().contains(MASTER_1));
ConnectPoint connectPoint = traceSuccess.getGroupOuputs(ARP_FLOW_DEVICE).get(0).getOutput();
@@ -303,6 +304,22 @@
}
+ /**
+ * Test LLDP output to controller.
+ */
+ @Test
+ public void lldpToController() {
+ StaticPacketTrace traceSuccess = mngr.trace(PACKET_LLDP, LLDP_FLOW_CP);
+ assertNotNull("Trace should not be null", traceSuccess);
+ assertTrue("Trace should be successful",
+ traceSuccess.resultMessage().contains("Packet goes to the controller"));
+ assertTrue("Master should be Master1",
+ traceSuccess.resultMessage().contains(MASTER_1));
+ ConnectPoint connectPoint = traceSuccess.getGroupOuputs(LLDP_FLOW_DEVICE).get(0).getOutput();
+ assertEquals("Packet Should go to CONTROLLER", PortNumber.CONTROLLER, connectPoint.port());
+ log.info("trace {}", traceSuccess.resultMessage());
+ }
+
private StaticPacketTrace testSuccess(TrafficSelector packet, ConnectPoint in, DeviceId deviceId, ConnectPoint out,
int paths, int outputs) {
StaticPacketTrace traceSuccess = mngr.trace(packet, in);
@@ -365,6 +382,8 @@
} else if (deviceId.equals(HARDWARE_DEVICE_10)) {
return ImmutableList.of(HARDWARE_10_FLOW_ENTRY, HARDWARE_10_SECOND_FLOW_ENTRY,
HARDWARE_10_OUTPUT_FLOW_ENTRY);
+ } else if (deviceId.equals(LLDP_FLOW_DEVICE)) {
+ return ImmutableList.of(LLDP_FLOW_ENTRY);
}
return ImmutableList.of();
}