ONOS Network Troubleshooting System
Newest Commit changes:
1. Add unit tests.
2. Fix review comments.
3. Add support to BUCK.
Could you please make a Code Review, we wish to hear anything from you :)
Thank you very much!
----------------------------------------------------
ONOS Network Troubleshooting System
Modularity design. In present, include these tow module:
1. Routing Loop Detection
Welcome your contribution for more modules in the future...
Beijing University of Posts and Telecommunications
new: withdraw blackhole tracing for redesign;
fix obvious checkstyle problem.
Change-Id: Id6d3aa0bc00c8da8ac046e6903f17cfdf954d919
diff --git a/apps/network-troubleshoot/BUCK b/apps/network-troubleshoot/BUCK
new file mode 100644
index 0000000..612bff6
--- /dev/null
+++ b/apps/network-troubleshoot/BUCK
@@ -0,0 +1,13 @@
+BUNDLES = [
+ '//apps/network-troubleshoot/api:onos-apps-network-troubleshoot-api',
+ '//apps/network-troubleshoot/cli:onos-apps-network-troubleshoot-cli',
+ '//apps/network-troubleshoot/core:onos-apps-network-troubleshoot-core',
+]
+
+onos_app (
+ title = 'Network TroubleShoot SubSystem',
+ description = 'ONOS Network TroubleShoot SubSystem',
+ category = 'Core',
+ url = 'https://wiki.onosproject.org/display/ONOS/Network+TroubleShooting+Module',
+ included_bundles = BUNDLES,
+)
diff --git a/apps/network-troubleshoot/api/BUCK b/apps/network-troubleshoot/api/BUCK
new file mode 100644
index 0000000..b6fdc64
--- /dev/null
+++ b/apps/network-troubleshoot/api/BUCK
@@ -0,0 +1,12 @@
+COMPILE_DEPS = [
+ '//lib:CORE_DEPS',
+]
+
+TEST_DEPS = [
+ '//lib:TEST_ADAPTERS',
+]
+
+osgi_jar_with_tests (
+ deps = COMPILE_DEPS,
+ test_deps = TEST_DEPS,
+)
diff --git a/apps/network-troubleshoot/api/pom.xml b/apps/network-troubleshoot/api/pom.xml
new file mode 100644
index 0000000..0d5cfcc
--- /dev/null
+++ b/apps/network-troubleshoot/api/pom.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2014-present Open Networking Laboratory
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-network-troubleshoot</artifactId>
+ <version>1.10.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>onos-network-troubleshoot-api</artifactId>
+ <packaging>bundle</packaging>
+
+ <description>ONOS Network TroubleShoot SubSystem API</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onlab-junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/apps/network-troubleshoot/api/src/main/java/org/onosproject/fnl/base/NetworkDiagnosticUtils.java b/apps/network-troubleshoot/api/src/main/java/org/onosproject/fnl/base/NetworkDiagnosticUtils.java
new file mode 100644
index 0000000..0a0d796
--- /dev/null
+++ b/apps/network-troubleshoot/api/src/main/java/org/onosproject/fnl/base/NetworkDiagnosticUtils.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2015-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.fnl.base;
+
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.HostId;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.criteria.Criterion;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Common utility functions and constants.
+ */
+public final class NetworkDiagnosticUtils {
+
+ private NetworkDiagnosticUtils() {
+ // no instantiation
+ }
+
+ /**
+ * Returns a list of flow entries sorted by priority in descending order.
+ *
+ * @param flowEntries flow entries to be sorted
+ * @return flow entries in descending order
+ */
+ public static List<FlowEntry> sortFlowTable(Iterable<FlowEntry> flowEntries) {
+
+ List<FlowEntry> flows = new ArrayList<>();
+ flowEntries.forEach(flows::add);
+
+ Collections.sort(flows,
+ (f1, f2) -> f2.priority() - f1.priority());
+ return flows;
+ }
+
+ /**
+ * Returns a list of match fields sorted by their types in ascending order.
+ *
+ * @param criterionSet the criteria to be sorted
+ * @return the list of criteria in ascending order
+ */
+ public static List<Criterion> sortCriteria(Set<Criterion> criterionSet) {
+
+ List<Criterion> array = new ArrayList<>(criterionSet);
+ Collections.sort(array,
+ (c1, c2) -> c1.type().compareTo(c2.type()));
+ return array;
+ }
+
+ /**
+ * Returns true if the given connect point is a device point.
+ *
+ * @param connectPoint the connect point to be checked
+ * @return true if the connect point is a device point
+ */
+ public static boolean isDevice(ConnectPoint connectPoint) {
+ return connectPoint.elementId() instanceof DeviceId;
+ }
+
+ /**
+ * Returns true if the given connect point is a host point.
+ *
+ * @param connectPoint the connect point to be checked
+ * @return true if the connect point is a host point
+ */
+ public static boolean isHost(ConnectPoint connectPoint) {
+ // TODO - not debug yet
+ return connectPoint.elementId() instanceof HostId;
+ }
+}
diff --git a/apps/network-troubleshoot/api/src/main/java/org/onosproject/fnl/base/TsLoopPacket.java b/apps/network-troubleshoot/api/src/main/java/org/onosproject/fnl/base/TsLoopPacket.java
new file mode 100644
index 0000000..cb7d455
--- /dev/null
+++ b/apps/network-troubleshoot/api/src/main/java/org/onosproject/fnl/base/TsLoopPacket.java
@@ -0,0 +1,409 @@
+/*
+ * Copyright 2015-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.fnl.base;
+
+import org.onosproject.fnl.intf.NetworkAnomaly;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.EthCriterion;
+import org.onosproject.net.flow.criteria.EthTypeCriterion;
+import org.onosproject.net.flow.criteria.IPCriterion;
+import org.onosproject.net.flow.criteria.IPDscpCriterion;
+import org.onosproject.net.flow.criteria.IPEcnCriterion;
+import org.onosproject.net.flow.criteria.IPProtocolCriterion;
+import org.onosproject.net.flow.criteria.PortCriterion;
+import org.onosproject.net.flow.criteria.TcpPortCriterion;
+import org.onosproject.net.flow.criteria.UdpPortCriterion;
+import org.onosproject.net.flow.criteria.VlanIdCriterion;
+import org.onosproject.net.flow.criteria.VlanPcpCriterion;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Stack;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Collections;
+
+import static org.onosproject.fnl.intf.NetworkAnomaly.Type.LOOP;
+import static org.onosproject.fnl.base.TsLoopPacket.SetHeaderResult.SETHEADER_FAILURE_NULL;
+import static org.onosproject.fnl.base.TsLoopPacket.SetHeaderResult.SETHEADER_OVERRIDE;
+import static org.onosproject.fnl.base.TsLoopPacket.SetHeaderResult.SETHEADER_SUCCESS;
+import static org.onosproject.net.flow.criteria.Criteria.*;
+
+/**
+ * Virtual packet for Default Loop Checking.
+ */
+public final class TsLoopPacket implements NetworkAnomaly {
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ private static final String EOL = String.format("%n");
+ private static final String LINE =
+ EOL + "====================================================" + EOL;
+ private static final String HDR_FMT = EOL + "---------- %s ----------" + EOL;
+ private static final String LOOP_HEADER = makeHeader("Loop Header");
+ private static final String LOOP_FLOW_ENTRIES = makeHeader("Loop Flow Entries");
+ private static final String LOOP_LINKS = makeHeader("Loop Links");
+
+ private Map<Criterion.Type, Criterion> match;
+ private Stack<FlowEntry> pathFlow;
+ // when Upgrade, check to MAKE SURE it include just Link but not EdgeLink
+ private Stack<Link> pathLink;
+
+ /**
+ * Create an initial virtual packet inside for Loop Checking.
+ */
+ private TsLoopPacket() {
+ match = new HashMap<>();
+ pathFlow = new Stack<>();
+ pathLink = new Stack<>();
+ }
+
+ @Override
+ public Type type() {
+ return LOOP;
+ }
+
+ /**
+ * Represents the result of setting a header to virtual packet.
+ */
+ public enum SetHeaderResult {
+ /**
+ * Set header successfully.
+ */
+ SETHEADER_SUCCESS,
+
+ /**
+ * Set header successfully but override old value.
+ */
+ SETHEADER_OVERRIDE,
+
+ /**
+ * Fail to set Header because NULL value.
+ */
+ SETHEADER_FAILURE_NULL,
+
+ /**
+ * Fail to set Header, but reason is not defined, defined in advance.
+ */
+ SETHEADER_FAILURE
+ }
+
+ /**
+ * Creates and returns a new packet instance with the copied match fields.
+ *
+ * With hard-copied match fields, references to path flows and path links.
+ *
+ * @return new loop packet instance with the copied match fields
+ */
+ public TsLoopPacket copyPacketMatch() {
+
+ TsLoopPacket newOne = new TsLoopPacket();
+
+ newOne.pathFlow = this.pathFlow;
+ newOne.pathLink = this.pathLink;
+
+ Map<Criterion.Type, Criterion> m = newOne.match;
+
+ for (Map.Entry<Criterion.Type, Criterion> entry : this.match.entrySet()) {
+ Criterion.Type k = entry.getKey();
+ Criterion v = entry.getValue();
+
+ switch (k) {
+ case IN_PORT:
+ m.put(k, matchInPort(((PortCriterion) v).port()));
+ break;
+ case ETH_SRC: // At present, not support Ethernet mask (ONOS?)
+ m.put(k, matchEthSrc(((EthCriterion) v).mac()));
+ break;
+ case ETH_DST: // At present, not support Ethernet mask (ONOS?)
+ m.put(k, matchEthDst(((EthCriterion) v).mac()));
+ break;
+ case ETH_TYPE:
+ m.put(k, matchEthType(((EthTypeCriterion) v).ethType()));
+ break;
+ case VLAN_VID: // At present, not support VLAN mask (ONOS?)
+ m.put(k, matchVlanId(((VlanIdCriterion) v).vlanId()));
+ break;
+ case VLAN_PCP:
+ m.put(k, matchVlanPcp(((VlanPcpCriterion) v).priority()));
+ break;
+ case IPV4_SRC:
+ m.put(k, matchIPSrc(((IPCriterion) v).ip()));
+ break;
+ case IPV4_DST:
+ m.put(k, matchIPDst(((IPCriterion) v).ip()));
+ break;
+ case IP_PROTO:
+ m.put(k, matchIPProtocol(((IPProtocolCriterion) v).protocol()));
+ break;
+ case IP_DSCP: // can't be supported by now
+ m.put(k, matchIPDscp(((IPDscpCriterion) v).ipDscp()));
+ break;
+ case IP_ECN: // can't be supported by now
+ m.put(k, matchIPEcn(((IPEcnCriterion) v).ipEcn()));
+ break;
+ case TCP_SRC:
+ m.put(k, matchTcpSrc(((TcpPortCriterion) v).tcpPort()));
+ break;
+ case TCP_DST:
+ m.put(k, matchTcpDst(((TcpPortCriterion) v).tcpPort()));
+ break;
+ case UDP_SRC:
+ m.put(k, matchUdpSrc(((UdpPortCriterion) v).udpPort()));
+ break;
+ case UDP_DST:
+ m.put(k, matchUdpDst(((UdpPortCriterion) v).udpPort()));
+ break;
+ default: //can't be supported by OF1.0
+ log.debug("{} can't be supported by OF1.0", k);
+ break;
+ }
+ }
+ return newOne;
+ }
+
+ /**
+ * Sets the given criterion as a packet header field.
+ *
+ * @param criterion as packet header field
+ * @return the result of set action
+ */
+ public SetHeaderResult setHeader(Criterion criterion) {
+
+ if (criterion == null) {
+ return SETHEADER_FAILURE_NULL;
+ }
+
+ boolean hasKey = match.containsKey(criterion.type());
+
+ match.put(criterion.type(), criterion);
+
+ return hasKey ? SETHEADER_OVERRIDE : SETHEADER_SUCCESS;
+ }
+
+ /**
+ * Deletes a packet header field by the designated header type.
+ *
+ * @param criterionType as packet header type
+ * @return true, if packet contained the corresponding type of header;
+ * false, otherwise
+ */
+ public boolean delHeader(Criterion.Type criterionType) {
+ return match.remove(criterionType) != null;
+ }
+
+ /**
+ * Returns a packet header field value by the designated header type.
+ *
+ * Returns null if the field does not exist.
+ *
+ * @param criterionType as packet header type
+ * @return the packet header field value; may be null
+ */
+ public Criterion getHeader(Criterion.Type criterionType) {
+ return match.get(criterionType);
+ }
+
+ /**
+ * Returns true if there is the type of header field in the packet.
+ *
+ * @param criterionType packet header type
+ * @return true if the field exists; false otherwise
+ */
+ public boolean headerExists(Criterion.Type criterionType) {
+ return match.containsKey(criterionType);
+ }
+
+ /**
+ * Pushes the given flow entry onto the path flow stack.
+ * Packet matches this entry in specific switch hop.
+ *
+ * @param entry the matched entry
+ */
+ public void pushPathFlow(FlowEntry entry) {
+ pathFlow.push(entry);
+ }
+
+ /**
+ * Pops a FlowEntry from path flow entry stack.
+ */
+ public void popPathFlow() {
+ pathFlow.pop();
+ }
+
+ /**
+ * Returns links in the path which the packet passes through.
+ *
+ * @return an iterator over the set of links
+ */
+ public Iterator<Link> getPathLink() {
+ return pathLink.iterator();
+ }
+
+ /**
+ * Adds a Link to path link list.
+ * Packet goes through this link between two switches.
+ *
+ * @param link The link through which the packet go
+ */
+ public void pushPathLink(Link link) {
+ // TODO - need CPY link manual?
+ pathLink.push(link);
+ }
+
+ /**
+ * Removes a Link from path link list.
+ */
+ public void popPathLink() {
+ pathLink.pop();
+ }
+
+ /**
+ * Returns true if the packet passed through the specific device.
+ *
+ * @param deviceId identify of the divice to test
+ * @return true if packet passed through the specific device;
+ * false otherwise
+ */
+ public boolean isPassedDevice(DeviceId deviceId) {
+ for (Link linkTemp : pathLink) {
+ if (deviceId.equals(linkTemp.src().deviceId())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns the IN_PORT header field of the packet.
+ *
+ * Attention:
+ * IN_PORT field will be changed when packet goes into the next switch hop.
+ *
+ * @return a port criterion object.
+ */
+ public PortCriterion getInport() {
+ // TODO - check IN_PORT or IN_PHY_PORT
+ return (PortCriterion) match.get(Criterion.Type.IN_PORT);
+ }
+
+ /**
+ * Creates and returns a loop packet instance with given Match Fields.
+ *
+ * Returns null,
+ * whenever SetHeader_FAILURE or SETHEADER_FAILURE_NULL happened.
+ *
+ * @param criteria match field of one flow entry
+ * @param collision as return value;
+ * true, if criteria contain multiple ones with same type
+ * @return a new loop packet instance; may be null
+ */
+ public static TsLoopPacket matchBuilder(Iterable<Criterion> criteria,
+ TsReturn<Boolean> collision) {
+
+ if (null != collision) {
+ collision.setValue(false);
+ }
+
+ TsLoopPacket pkt = new TsLoopPacket();
+
+ for (Criterion criterion : criteria) {
+
+ SetHeaderResult ret = pkt.setHeader(criterion);
+
+ if (SETHEADER_SUCCESS == ret) {
+ //TODO - in the future, we may need to resolve this condition
+ } else if (SETHEADER_OVERRIDE == ret) {
+ if (null != collision) {
+ collision.setValue(true);
+ }
+ } else { // SetHeader_FAILURE or SetHeader_FAILURE_NULL
+ pkt = null;
+ break;
+ }
+ }
+
+ return pkt;
+ }
+
+ /**
+ * Hands in the header of virtual packet one by one.
+ * Let the header go up through every layer of recursion.
+ * It is called when a loop is discovered.
+ *
+ * @param loopPkt virtual packet that will trigger Loop Storm
+ */
+ public void handInLoopMatch(TsLoopPacket loopPkt) {
+ match = loopPkt.match;
+ }
+
+ /**
+ * Resets the path link and path flow structures.
+ * And initializing the path flow with the gicen flow entry.
+ *
+ * @param firstEntry the flow entry from which this packet is built
+ */
+ public void resetLinkFlow(FlowEntry firstEntry) {
+ pathLink = new Stack<>();
+ pathFlow = new Stack<>();
+ pathFlow.push(firstEntry);
+ }
+
+ private static String makeHeader(String title) {
+ return String.format(HDR_FMT, title);
+ }
+
+ /**
+ * Returns a multi-line string representation of this loop packet instance.
+ *
+ * @return formatted string
+ */
+ @Override
+ public String toString() {
+ StringBuilder me = new StringBuilder();
+
+ me.append(LINE);
+
+ me.append(LOOP_HEADER);
+
+ List<Criterion> criteria = new ArrayList<>(match.values());
+ Collections.sort(criteria, (o1, o2) -> o1.type().compareTo(o2.type()));
+
+ for (Criterion c : criteria) {
+ me.append(c).append(EOL);
+ }
+
+ me.append(LOOP_FLOW_ENTRIES);
+
+ for (FlowEntry flow : pathFlow) {
+ me.append(flow).append(EOL);
+ }
+
+ me.append(LOOP_LINKS);
+
+ for (Link l : pathLink) {
+ me.append(l).append(EOL);
+ }
+
+ return me.toString();
+ }
+}
diff --git a/apps/network-troubleshoot/api/src/main/java/org/onosproject/fnl/base/TsReturn.java b/apps/network-troubleshoot/api/src/main/java/org/onosproject/fnl/base/TsReturn.java
new file mode 100644
index 0000000..f0f56d5
--- /dev/null
+++ b/apps/network-troubleshoot/api/src/main/java/org/onosproject/fnl/base/TsReturn.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2015-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.fnl.base;
+
+/**
+ * Represents an additional value that a caller may pass to a method.
+ * Be filled in by the called method.
+ *
+ * Used as an extra return value.
+ *
+ * @param <M> the class of expected return value
+ */
+public final class TsReturn<M> {
+ private M ret;
+
+ /**
+ * Sets the value of this instance.
+ *
+ * @param value the value to set
+ */
+ public void setValue(M value) {
+ ret = value;
+ }
+
+ /**
+ * Returns the value of this instance.
+ *
+ * @return the value
+ */
+ public M getValue() {
+ return ret;
+ }
+
+ /**
+ * Returns true if the value has been set.
+ * Generally, if setValue() has not been invoked,
+ * the value will not be present (i.e. null).
+ *
+ * @return true, if ret is present;
+ * false, otherwise
+ */
+ public boolean isPresent() {
+ return ret != null;
+ }
+}
diff --git a/apps/network-troubleshoot/api/src/main/java/org/onosproject/fnl/base/package-info.java b/apps/network-troubleshoot/api/src/main/java/org/onosproject/fnl/base/package-info.java
new file mode 100644
index 0000000..aca2d7b
--- /dev/null
+++ b/apps/network-troubleshoot/api/src/main/java/org/onosproject/fnl/base/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Base and tool classes used in troubleshooting algorithms.
+ */
+package org.onosproject.fnl.base;
diff --git a/apps/network-troubleshoot/api/src/main/java/org/onosproject/fnl/intf/NetworkAnomaly.java b/apps/network-troubleshoot/api/src/main/java/org/onosproject/fnl/intf/NetworkAnomaly.java
new file mode 100644
index 0000000..aa754a7
--- /dev/null
+++ b/apps/network-troubleshoot/api/src/main/java/org/onosproject/fnl/intf/NetworkAnomaly.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.fnl.intf;
+
+/**
+ * Result of network anomaly diagnosis.
+ */
+public interface NetworkAnomaly {
+
+ /**
+ * Returns the type of anomaly result.
+ *
+ * @return the type of anomaly
+ */
+ Type type();
+
+ /**
+ * Represents anomaly types.
+ */
+ enum Type {
+
+ /**
+ * Packets round among several devices with several forwarding entries.
+ *
+ * Routing loops.
+ */
+ LOOP
+ }
+}
diff --git a/apps/network-troubleshoot/api/src/main/java/org/onosproject/fnl/intf/NetworkDiagnostic.java b/apps/network-troubleshoot/api/src/main/java/org/onosproject/fnl/intf/NetworkDiagnostic.java
new file mode 100644
index 0000000..55122f1
--- /dev/null
+++ b/apps/network-troubleshoot/api/src/main/java/org/onosproject/fnl/intf/NetworkDiagnostic.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2015-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.fnl.intf;
+
+import java.util.Set;
+
+/**
+ * Provide algorithms or methods to diagnose network.
+ *
+ * Strategy Pattern.
+ */
+public interface NetworkDiagnostic {
+
+ /**
+ * Checks for and returns all corresponding anomalies.
+ * An empty set is returned if there are no anomalies.
+ *
+ * @return the set of all corresponding anomalies; may be empty
+ */
+ Set<NetworkAnomaly> findAnomalies();
+
+ /**
+ * Returns the type of diagnostic.
+ *
+ * @return the type of diagnostic
+ */
+ Type type();
+
+ /**
+ * Represents diagnostic types.
+ */
+ enum Type {
+
+ /**
+ * Packets round among several devices with several forwarding entries.
+ *
+ * Routing loops.
+ */
+ LOOP
+ }
+}
diff --git a/apps/network-troubleshoot/api/src/main/java/org/onosproject/fnl/intf/NetworkDiagnosticService.java b/apps/network-troubleshoot/api/src/main/java/org/onosproject/fnl/intf/NetworkDiagnosticService.java
new file mode 100644
index 0000000..a3086ba
--- /dev/null
+++ b/apps/network-troubleshoot/api/src/main/java/org/onosproject/fnl/intf/NetworkDiagnosticService.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2015-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.fnl.intf;
+
+import org.onosproject.fnl.intf.NetworkDiagnostic.Type;
+
+import java.util.Set;
+
+/**
+ * Network Troubleshooting Core Service.
+ */
+public interface NetworkDiagnosticService {
+
+ /**
+ * Checks for and returns all registered kinds of network anomalies.
+ * An empty set is returned if there are no anomalies found.
+ *
+ * @return all discovered anomalies; may be empty
+ */
+ Set<NetworkAnomaly> findAnomalies();
+
+ /**
+ * Checks for and returns the specific kind of network anomalies.
+ * <p>
+ * An empty set is returned if there is no anomaly of specific type,
+ * or there is no diagnostic of specific type.
+ *
+ * @return the specific kind of anomalies; may be empty
+ */
+
+ /**
+ * Checks for and returns the specific type of network anomalies.
+ * <p>
+ * An empty set is returned if there is no anomaly of specific type,
+ * or there is no diagnostic of specific type.
+ *
+ * @param type the type of network anomalies
+ * @return the specific kind of anomalies; may be empty
+ */
+ Set<NetworkAnomaly> findAnomalies(Type type);
+
+ /**
+ * Registers the specified diagnostic module with the service.
+ *
+ * Each diagnostic type can have only one module,
+ * and previous one will be removed.
+ *
+ * @param diagnostic an instance of class implemented NetworkDiagnostic
+ */
+ void register(NetworkDiagnostic diagnostic);
+
+ /**
+ * Unregisters the specified diagnostic module form the service.
+ *
+ * @param diagnostic diagnostic module to be removed
+ * @return true if the module existed before and has been removed
+ * successfully; false otherwise
+ */
+ boolean unregister(NetworkDiagnostic diagnostic);
+}
diff --git a/apps/network-troubleshoot/api/src/main/java/org/onosproject/fnl/intf/package-info.java b/apps/network-troubleshoot/api/src/main/java/org/onosproject/fnl/intf/package-info.java
new file mode 100644
index 0000000..4f7a3b8
--- /dev/null
+++ b/apps/network-troubleshoot/api/src/main/java/org/onosproject/fnl/intf/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Base interfaces for Network Troubleshooting SubSystem.
+ */
+package org.onosproject.fnl.intf;
diff --git a/apps/network-troubleshoot/api/src/test/java/org/onosproject/fnl/base/TsReturnTest.java b/apps/network-troubleshoot/api/src/test/java/org/onosproject/fnl/base/TsReturnTest.java
new file mode 100644
index 0000000..3d7cacd
--- /dev/null
+++ b/apps/network-troubleshoot/api/src/test/java/org/onosproject/fnl/base/TsReturnTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2015-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.fnl.base;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Unit Tests for TsReturn class.
+ */
+public class TsReturnTest {
+
+ private static final boolean OLD_BOOLEAN_EXAMPLE = false;
+ private static final boolean NEW_BOOLEAN_EXAMPLE = true;
+ private static final int OLD_INTEGER_EXAMPLE = 7181;
+ private static final int NEW_INTEGER_EXAMPLE = 1355;
+
+
+ @Before
+ public void setUp() {
+ // do nothing
+ }
+
+ @After
+ public void tearDown() {
+ // do nothing
+ }
+
+ @Test
+ public void testPresent() {
+ TsReturn<Boolean> bool = new TsReturn<>();
+ assertFalse(bool.isPresent());
+
+ bool.setValue(NEW_BOOLEAN_EXAMPLE);
+ assertTrue(bool.isPresent());
+ }
+
+ @Test
+ public void testRoleReturnBoolean() {
+ TsReturn<Boolean> bool = new TsReturn<>();
+ bool.setValue(OLD_BOOLEAN_EXAMPLE);
+
+ Boolean oldValue = bool.getValue();
+ changeBoolean(bool);
+ Boolean newValue = bool.getValue();
+
+ assertNotSame(oldValue, newValue);
+ assertEquals(newValue, NEW_BOOLEAN_EXAMPLE);
+ }
+
+ @Test
+ public void testRoleReturnInteger() {
+ TsReturn<Integer> integer = new TsReturn<>();
+ integer.setValue(OLD_INTEGER_EXAMPLE);
+
+ Integer oldValue = integer.getValue();
+ changeInteger(integer);
+ Integer newValue = integer.getValue();
+
+ assertNotSame(oldValue, newValue);
+ assertEquals(newValue.intValue(), NEW_INTEGER_EXAMPLE);
+ }
+
+ private void changeBoolean(TsReturn bool) {
+ bool.setValue(NEW_BOOLEAN_EXAMPLE);
+ }
+
+ private void changeInteger(TsReturn integer) {
+ integer.setValue(NEW_INTEGER_EXAMPLE);
+ }
+}
\ No newline at end of file
diff --git a/apps/network-troubleshoot/app/app.xml b/apps/network-troubleshoot/app/app.xml
new file mode 100644
index 0000000..a81cf6d
--- /dev/null
+++ b/apps/network-troubleshoot/app/app.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2015-present Open Networking Laboratory
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<app name="org.onosproject.fnl.Network.Troubleshoot" category="Core" version="${project.version}"
+ title="Network TroubleShoot SubSystem"
+ originUrl="http://www.bupt.edu.cn" origin="FNLab, BUPT"
+ features="${project.artifactId}"
+ featuresRepo="mvn:${project.groupId}/${project.artifactId}/${project.version}/xml/features"
+ url="https://wiki.onosproject.org/display/ONOS/Network+TroubleShooting+Module">
+
+ <description>${project.description}</description>
+
+ <artifact>mvn:${project.groupId}/onos-network-troubleshoot-api/${project.version}</artifact>
+ <artifact>mvn:${project.groupId}/onos-network-troubleshoot-core/${project.version}</artifact>
+ <artifact>mvn:${project.groupId}/onos-network-troubleshoot-cli/${project.version}</artifact>
+
+</app>
diff --git a/apps/network-troubleshoot/app/features.xml b/apps/network-troubleshoot/app/features.xml
new file mode 100644
index 0000000..266db1e
--- /dev/null
+++ b/apps/network-troubleshoot/app/features.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--
+ ~ Copyright 2015-present Open Networking Laboratory
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0"
+ name="${project.artifactId}-${project.version}">
+
+ <feature name="${project.artifactId}" version="${project.version}"
+ description="${project.description}">
+
+ <bundle>mvn:${project.groupId}/onos-network-troubleshoot-api/${project.version}</bundle>
+ <bundle>mvn:${project.groupId}/onos-network-troubleshoot-core/${project.version}</bundle>
+ <bundle>mvn:${project.groupId}/onos-network-troubleshoot-cli/${project.version}</bundle>
+
+ </feature>
+
+</features>
diff --git a/apps/network-troubleshoot/app/pom.xml b/apps/network-troubleshoot/app/pom.xml
new file mode 100644
index 0000000..90bffa3
--- /dev/null
+++ b/apps/network-troubleshoot/app/pom.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2015-present Open Networking Laboratory
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-network-troubleshoot</artifactId>
+ <version>1.10.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>onos-network-troubleshoot-app</artifactId>
+ <packaging>pom</packaging>
+
+ <description>ONOS Network TroubleShoot SubSystem</description>
+
+ <dependencies>
+
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-network-troubleshoot-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-network-troubleshoot-cli</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-network-troubleshoot-core</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ </dependencies>
+
+</project>
diff --git a/apps/network-troubleshoot/cli/BUCK b/apps/network-troubleshoot/cli/BUCK
new file mode 100644
index 0000000..8f3fe1e
--- /dev/null
+++ b/apps/network-troubleshoot/cli/BUCK
@@ -0,0 +1,18 @@
+COMPILE_DEPS = [
+ '//lib:CORE_DEPS',
+ '//lib:org.apache.karaf.shell.console',
+ '//cli:onos-cli',
+# '//lib:org.apache.karaf.shell.console',
+# '//incubator/api:onos-incubator-api',
+# '//cli:onos-cli',
+# '//utils/rest:onlab-rest',
+# '//lib:javax.ws.rs-api',
+# '//utils/osgi:onlab-osgi',
+# '//core/store/serializers:onos-core-serializers',
+ '//apps/network-troubleshoot/api:onos-apps-network-troubleshoot-api',
+]
+
+osgi_jar_with_tests (
+ deps = COMPILE_DEPS,
+ import_packages = '*,org.onosproject.cli.net',
+)
diff --git a/apps/network-troubleshoot/cli/pom.xml b/apps/network-troubleshoot/cli/pom.xml
new file mode 100644
index 0000000..e46c143
--- /dev/null
+++ b/apps/network-troubleshoot/cli/pom.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2015-present Open Networking Laboratory
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-network-troubleshoot</artifactId>
+ <version>1.10.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>onos-network-troubleshoot-cli</artifactId>
+ <packaging>bundle</packaging>
+
+ <description>ONOS Network TroubleShoot SubSystem</description>
+
+ <dependencies>
+
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-network-troubleshoot-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <!-- for CLI command -->
+ <dependency>
+ <groupId>org.apache.karaf.shell</groupId>
+ <artifactId>org.apache.karaf.shell.console</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-cli</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ </dependencies>
+
+</project>
diff --git a/apps/network-troubleshoot/cli/src/main/java/org/onosproject/fnl/cli/TsAllAnomalies.java b/apps/network-troubleshoot/cli/src/main/java/org/onosproject/fnl/cli/TsAllAnomalies.java
new file mode 100644
index 0000000..a6c60c7
--- /dev/null
+++ b/apps/network-troubleshoot/cli/src/main/java/org/onosproject/fnl/cli/TsAllAnomalies.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.fnl.cli;
+
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.fnl.intf.NetworkDiagnosticService;
+
+/**
+ * Search for all types of network anomalies.
+ */
+@Command(scope = "onos",
+ name = "ts-all-anomalies",
+ description = "search all types of network anomalies once",
+ detailedDescription = "Report different information " +
+ "for specific type of anomalies.")
+public class TsAllAnomalies extends AbstractShellCommand {
+
+ @Override
+ protected void execute() {
+ NetworkDiagnosticService service = getService(NetworkDiagnosticService.class);
+
+ service.findAnomalies().forEach(a -> print(a.toString()));
+ }
+}
diff --git a/apps/network-troubleshoot/cli/src/main/java/org/onosproject/fnl/cli/TsCheckLoop.java b/apps/network-troubleshoot/cli/src/main/java/org/onosproject/fnl/cli/TsCheckLoop.java
new file mode 100644
index 0000000..bb928ab
--- /dev/null
+++ b/apps/network-troubleshoot/cli/src/main/java/org/onosproject/fnl/cli/TsCheckLoop.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2015-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.fnl.cli;
+
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.fnl.intf.NetworkDiagnostic;
+import org.onosproject.fnl.intf.NetworkDiagnosticService;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.host.HostService;
+import org.onosproject.net.link.LinkService;
+
+/**
+ * Search for all potential routing loops.
+ */
+@Command(scope = "onos",
+ name = "ts-check-loops",
+ description = "Check if there are some routing loops in the network",
+ detailedDescription = "Report the header of loop-trigger packet, " +
+ "DevicesIds and FlowEntries.")
+public class TsCheckLoop extends AbstractShellCommand {
+
+ @Override
+ protected void execute() {
+ NetworkDiagnosticService service = getService(NetworkDiagnosticService.class);
+
+ DeviceService ds = getService(DeviceService.class);
+ HostService hs = getService(HostService.class);
+ FlowRuleService frs = getService(FlowRuleService.class);
+ LinkService ls = getService(LinkService.class);
+
+ service.findAnomalies(NetworkDiagnostic.Type.LOOP)
+ .forEach(loop -> print(loop.toString()));
+ }
+}
diff --git a/apps/network-troubleshoot/cli/src/main/java/org/onosproject/fnl/cli/package-info.java b/apps/network-troubleshoot/cli/src/main/java/org/onosproject/fnl/cli/package-info.java
new file mode 100644
index 0000000..1ef3790
--- /dev/null
+++ b/apps/network-troubleshoot/cli/src/main/java/org/onosproject/fnl/cli/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * The CLI commands for checking specific network trouble.
+ */
+package org.onosproject.fnl.cli;
diff --git a/apps/network-troubleshoot/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/apps/network-troubleshoot/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
new file mode 100644
index 0000000..01c6028
--- /dev/null
+++ b/apps/network-troubleshoot/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ Copyright 2015-present Open Networking Laboratory
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
+ <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
+ <command>
+ <action class="org.onosproject.fnl.cli.TsAllAnomalies" />
+ </command>
+ <command>
+ <action class="org.onosproject.fnl.cli.TsCheckLoop" />
+ </command>
+ </command-bundle>
+</blueprint>
diff --git a/apps/network-troubleshoot/core/BUCK b/apps/network-troubleshoot/core/BUCK
new file mode 100644
index 0000000..1174519
--- /dev/null
+++ b/apps/network-troubleshoot/core/BUCK
@@ -0,0 +1,17 @@
+COMPILE_DEPS = [
+ '//lib:CORE_DEPS',
+ '//incubator/api:onos-incubator-api',
+# '//core/store/serializers:onos-core-serializers',
+ '//apps/network-troubleshoot/api:onos-apps-network-troubleshoot-api',
+]
+
+TEST_DEPS = [
+ '//lib:TEST_ADAPTERS',
+ '//utils/osgi:onlab-osgi-tests',
+ '//incubator/api:onos-incubator-api-tests',
+]
+
+osgi_jar_with_tests (
+ deps = COMPILE_DEPS,
+ test_deps = TEST_DEPS,
+)
diff --git a/apps/network-troubleshoot/core/pom.xml b/apps/network-troubleshoot/core/pom.xml
new file mode 100644
index 0000000..43f0281
--- /dev/null
+++ b/apps/network-troubleshoot/core/pom.xml
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2015-present Open Networking Laboratory
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-network-troubleshoot</artifactId>
+ <version>1.10.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>onos-network-troubleshoot-core</artifactId>
+ <packaging>bundle</packaging>
+
+ <description>ONOS Network TroubleShoot SubSystem</description>
+
+ <dependencies>
+
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-network-troubleshoot-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <!-- for component config -->
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ </dependency>
+
+ <!-- Test -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onlab-junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest-library</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymock</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ </dependencies>
+
+</project>
diff --git a/apps/network-troubleshoot/core/src/main/java/org/onosproject/fnl/impl/DefaultCheckLoop.java b/apps/network-troubleshoot/core/src/main/java/org/onosproject/fnl/impl/DefaultCheckLoop.java
new file mode 100644
index 0000000..3634683
--- /dev/null
+++ b/apps/network-troubleshoot/core/src/main/java/org/onosproject/fnl/impl/DefaultCheckLoop.java
@@ -0,0 +1,617 @@
+/*
+ * Copyright 2015-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.fnl.impl;
+
+import org.onlab.packet.IpPrefix;
+import org.onosproject.fnl.intf.NetworkAnomaly;
+import org.onosproject.fnl.intf.NetworkDiagnostic;
+import org.onosproject.fnl.base.TsLoopPacket;
+import org.onosproject.fnl.base.TsReturn;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.EthTypeCriterion;
+import org.onosproject.net.flow.criteria.IPCriterion;
+import org.onosproject.net.flow.criteria.IPProtocolCriterion;
+import org.onosproject.net.flow.criteria.PortCriterion;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
+import org.onosproject.net.host.HostService;
+import org.onosproject.net.link.LinkService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Iterator;
+import java.util.HashMap;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.packet.EthType.EtherType.IPV4;
+import static org.onlab.packet.EthType.EtherType.VLAN;
+import static org.onosproject.fnl.base.NetworkDiagnosticUtils.isDevice;
+import static org.onosproject.fnl.base.NetworkDiagnosticUtils.sortCriteria;
+import static org.onosproject.fnl.base.NetworkDiagnosticUtils.sortFlowTable;
+import static org.onosproject.fnl.base.TsLoopPacket.matchBuilder;
+import static org.onosproject.fnl.intf.NetworkDiagnostic.Type.LOOP;
+import static org.onosproject.net.flow.FlowEntry.FlowEntryState.ADDED;
+import static org.onosproject.net.flow.criteria.Criteria.matchInPort;
+import static org.onosproject.net.flow.criteria.Criterion.Type.ETH_TYPE;
+import static org.onosproject.net.flow.criteria.Criterion.Type.IN_PORT;
+import static org.onosproject.net.flow.criteria.Criterion.Type.IP_PROTO;
+
+/**
+ * Loop Checking Diagnostic Implementation.
+ *
+ * Strategy Pattern.
+ */
+public class DefaultCheckLoop implements NetworkDiagnostic {
+
+ private static final int IP_PROTO_TCP_TS = 6;
+ private static final int IP_PROTO_UDP_TS = 17;
+
+ private static final String E_CANNOT_HANDLE = "can not handle {} now.";
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ private final DeviceService deviceService;
+ private final HostService hostService;
+ private final FlowRuleService flowRuleService;
+ private final LinkService linkService;
+
+
+ private Map<DeviceId, Device> deviceInfo;
+ private Map<DeviceId, Iterable<FlowEntry>> flowEntryInfo;
+ private Set<Device> accessDevices;
+
+ //conventionally used by tsGetEgressLinks()
+ private Map<DeviceId, Set<Link>> egressLinkInfo;
+
+ //Two below are hot data in checking process.
+ private Set<NetworkAnomaly> loops;
+ private Set<DeviceId> excludeDeviceId;
+
+ /**
+ * Creates and returns an instance of Loop Checking algorithm module.
+ *
+ * @param ds reference of DeviceService
+ * @param hs reference of HostService
+ * @param frs reference of FlowRuleService
+ * @param ls reference of LinkService
+ */
+ public DefaultCheckLoop(DeviceService ds,
+ HostService hs,
+ FlowRuleService frs,
+ LinkService ls) {
+ checkNotNull(ds, "DeviceService cannot be null");
+ checkNotNull(hs, "HostService cannot be null");
+ checkNotNull(frs, "FlowRuleService cannot be null");
+ checkNotNull(ls, "LinkService cannot be null");
+
+ deviceService = ds;
+ hostService = hs;
+ flowRuleService = frs;
+ linkService = ls;
+ }
+
+ /**
+ * Checks for loops and returns any that were found.
+ * An empty set is returned if there are no loops.
+ *
+ * @return the set of loops; may be empty
+ */
+ @Override
+ public Set<NetworkAnomaly> findAnomalies() {
+ return findLoop();
+ }
+
+ @Override
+ public Type type() {
+ return LOOP;
+ }
+
+ /**
+ * Enter of the loop checking algorithm.
+ *
+ * @return the set of loop results; empty, if there is no loop
+ */
+ private Set<NetworkAnomaly> findLoop() {
+
+ getNetworkSnapshot();
+
+ loops = new HashSet<>();
+ excludeDeviceId = new HashSet<>();
+
+ for (Device device : accessDevices) {
+ if (excludeDeviceId.contains(device.id())) {
+ continue;
+ }
+
+
+ List<FlowEntry> availableFlowEntries = new ArrayList<>();
+
+ flowEntryInfo.get(device.id()).forEach(flowEntry -> {
+ if (flowEntry.state() == ADDED) {
+ availableFlowEntries.add(flowEntry);
+ }
+ });
+
+ List<FlowEntry> sortedFlowEntries = sortFlowTable(availableFlowEntries);
+
+
+ for (FlowEntry flow : sortedFlowEntries) {
+
+ TsLoopPacket pkt =
+ matchBuilder(flow.selector().criteria(), null);
+
+ pkt.pushPathFlow(flow);
+
+ List<Instruction> inst = flow.treatment().immediate();
+
+ for (Instruction instOne : inst) {
+ // Attention !!!
+ // if you would like to modify the code here,
+ // please MAKE VERY SURE that you are clear with
+ // the relationship of any invoked methods, and
+ // the relationship of params which are passed in and out.
+ processOneInstruction(instOne, device.id(),
+ null, pkt, true, flow);
+ }
+ }
+ }
+
+ // TODO - avoid two-hop LOOP
+
+ // TODO - another clean operations
+
+ return loops;
+ }
+
+ /**
+ * Iterate one by one at switch hops.
+ * Return whether we discover a Loop now or not.
+ *
+ * When flows form a loop,
+ * pkt is also a return value indicating the loop header.
+ *
+ * @param deviceId the device needed to be checked
+ * @param pkt virtual packet forwarded by switches
+ * @return true if a loop is discovered
+ */
+ private boolean matchDeviceFlows(DeviceId deviceId, TsLoopPacket pkt) {
+ if (pkt.isPassedDevice(deviceId)) {
+ return true; // Attention: pkt should be held outside
+ }
+
+
+ List<FlowEntry> availableFlowEntries = new ArrayList<>();
+
+ flowEntryInfo.get(deviceId).forEach(flowEntry -> {
+ if (flowEntry.state() == ADDED) {
+ availableFlowEntries.add(flowEntry);
+ }
+ });
+
+ List<FlowEntry> sortedFlowEntries = sortFlowTable(availableFlowEntries);
+
+
+ for (FlowEntry flowEntry : sortedFlowEntries) {
+ TsReturn<Boolean> isBigger = new TsReturn<>();
+ TsLoopPacket newPkt = pkt.copyPacketMatch();
+
+ if (!matchAndAddFlowEntry(flowEntry, newPkt, isBigger)) {
+ continue;
+ }
+
+ newPkt.pushPathFlow(flowEntry);
+ // no need to popPathFlow(),
+ // because we will drop this newPkt, and copy pkt again
+
+ for (Instruction instOne : flowEntry.treatment().immediate()) {
+ // Attention !!!
+ // if you would like to modify the code here,
+ // please MAKE VERY SURE that you are clear with
+ // the relationship of any invoked methods, and
+ // the relationship of params which are passed in and out.
+ if (processOneInstruction(instOne, deviceId,
+ pkt, newPkt, false, null)) {
+ return true;
+ }
+ }
+
+ newPkt.popPathFlow();
+
+ if (!isBigger.getValue()) {
+ break;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Process one of every instructions.
+ *
+ * The isFindLoop should be true if it is invoked by findLoop method,
+ * and be false if it is invoked by matchDeviceFlows method.
+ *
+ * @param instOne the instruction to be processed
+ * @param currentDeviceId id of the device we are now in
+ * @param initPkt the packet before being copied
+ * @param matchedPkt the packet which matched the flow entry,
+ * to which this instruction belongs
+ * @param isFindLoop indicate if it is invoked by findLoop method
+ * @param firstEntry the flow entry from which the packet is generated
+ * @return true, if a loop is discovered;
+ * false, 1. invoked by matchDeviceFlows method, and detected no loop;
+ * 2. invoked by findLoop method
+ */
+ private boolean processOneInstruction(Instruction instOne,
+ DeviceId currentDeviceId,
+ TsLoopPacket initPkt,
+ TsLoopPacket matchedPkt,
+ boolean isFindLoop,
+ FlowEntry firstEntry) {
+ if (isFindLoop) {
+ checkArgument(initPkt == null,
+ "initPkt is not null, while isFindLoop is true.");
+ } else {
+ checkArgument(firstEntry == null,
+ "firstEntry is not null, while isFindLoop is false.");
+ }
+
+
+ Instruction.Type type = instOne.type();
+ switch (type) {
+ case L2MODIFICATION:
+ //TODO - modify the L2 header of virtual packet
+ log.warn(E_CANNOT_HANDLE, type);
+ break;
+ case L3MODIFICATION:
+ //TODO - modify the L3 header of virtual packet
+ log.warn(E_CANNOT_HANDLE, type);
+ break;
+ case L4MODIFICATION:
+ //TODO - modify the L4 header of virtual packet
+ log.warn(E_CANNOT_HANDLE, type);
+ break;
+ case OUTPUT:
+ if (processOneOutputInstruction(instOne, currentDeviceId,
+ initPkt, matchedPkt, isFindLoop, firstEntry)) {
+ return true;
+ }
+ break;
+ default:
+ log.error("Can't process this type of instruction: {} now.",
+ instOne.type());
+ break;
+ }
+ return false;
+ }
+
+ /**
+ * Process one output instruction.
+ *
+ * Params are passed from processOneInstruction directly,
+ * and obey the same rules.
+ *
+ * @param instOne the instruction to be processed
+ * @param currentDeviceId id of the device we are now in
+ * @param initPkt the packet before being copied
+ * @param matchedPkt the packet which matched the flow entry,
+ * to which this instruction belongs
+ * @param isFindLoop indicate if it is invoked by findLoop method
+ * @param firstEntry the flow entry from which the packet is generated
+ * @return true, if a loop is discovered;
+ * false, 1. invoked by matchDeviceFlows method, and detected no loop;
+ * 2. invoked by findLoop method
+ */
+ private boolean processOneOutputInstruction(Instruction instOne,
+ DeviceId currentDeviceId,
+ TsLoopPacket initPkt,
+ TsLoopPacket matchedPkt,
+ boolean isFindLoop,
+ FlowEntry firstEntry) {
+ OutputInstruction instOutput = (OutputInstruction) instOne;
+ PortNumber instPort = instOutput.port();
+
+ if (!instPort.isLogical()) {
+ // single OUTPUT - NIC or normal port
+
+ Set<Link> dstLink = tsGetEgressLinks(
+ new ConnectPoint(currentDeviceId, instPort));
+ if (!dstLink.isEmpty()) {
+
+ // TODO - now, just deal with the first destination.
+ // will there be more destinations?
+
+ Link dstThisLink = dstLink.iterator().next();
+ ConnectPoint dstPoint = dstThisLink.dst();
+
+ // check output to devices only (output to a host is normal)
+ if (isDevice(dstPoint)) {
+ PortCriterion oldInPort =
+ updatePktInportPerHop(matchedPkt, dstPoint);
+ matchedPkt.pushPathLink(dstThisLink);
+ TsLoopPacket newNewPkt = matchedPkt.copyPacketMatch();
+
+ boolean loopFound =
+ matchDeviceFlows(dstPoint.deviceId(), newNewPkt);
+ if (isFindLoop) {
+ if (loopFound) {
+ loops.add(newNewPkt);
+ updateExcludeDeviceSet(newNewPkt);
+ }
+ matchedPkt.resetLinkFlow(firstEntry);
+ } else {
+ if (loopFound) {
+ initPkt.handInLoopMatch(newNewPkt);
+ return true;
+ }
+ matchedPkt.popPathLink();
+ }
+ restorePktInportPerHop(matchedPkt, oldInPort);
+ }
+ } else {
+ if (!isFindLoop) {
+ //TODO - NEED
+ log.warn("no link connecting at device {}, port {}",
+ currentDeviceId, instPort);
+ }
+ }
+ } else if (instPort.equals(PortNumber.IN_PORT)) {
+ //TODO - in the future,
+ // we may need to resolve this condition 1
+ log.warn("can not handle {} port now.", PortNumber.IN_PORT);
+ } else if (instPort.equals(PortNumber.NORMAL) ||
+ instPort.equals(PortNumber.FLOOD) ||
+ instPort.equals(PortNumber.ALL)) {
+ //TODO - in the future,
+ // we may need to resolve this condition 2
+ log.warn("can not handle {}/{}/{} now.",
+ PortNumber.NORMAL, PortNumber.FLOOD, PortNumber.ALL);
+ }
+ return false;
+ }
+
+ private void updateExcludeDeviceSet(TsLoopPacket loopPkt) {
+ Iterator<Link> iter = loopPkt.getPathLink();
+ while (iter.hasNext()) {
+ excludeDeviceId.add(iter.next().src().deviceId());
+ }
+ }
+
+ private PortCriterion updatePktInportPerHop(TsLoopPacket oldPkt,
+ ConnectPoint dstPoint) {
+
+ PortCriterion inPort = oldPkt.getInport();
+ PortCriterion oldInPort =
+ null != inPort ? (PortCriterion) matchInPort(inPort.port()) : null;
+ // TODO - check - if it really copies this object
+
+ oldPkt.setHeader(matchInPort(dstPoint.port()));
+
+ return oldInPort;
+ }
+
+ private void restorePktInportPerHop(TsLoopPacket pkt,
+ PortCriterion oldInPort) {
+ if (oldInPort == null) {
+ pkt.delHeader(IN_PORT);
+ } else {
+ pkt.setHeader(oldInPort);
+ }
+ }
+
+ private void getNetworkSnapshot() {
+ deviceInfo = new HashMap<>();
+ deviceService.getDevices().forEach(d -> deviceInfo.put(d.id(), d));
+
+ flowEntryInfo = new HashMap<>();
+ deviceInfo.keySet().forEach(id ->
+ flowEntryInfo.put(id, flowRuleService.getFlowEntries(id)));
+
+ egressLinkInfo = new HashMap<>();
+ deviceInfo.keySet().forEach(id ->
+ egressLinkInfo.put(id, linkService.getDeviceEgressLinks(id)));
+
+ accessDevices = new HashSet<>();
+ hostService.getHosts().forEach(h ->
+ accessDevices.add(deviceInfo.get(h.location().deviceId())));
+ }
+
+ private Set<Link> tsGetEgressLinks(ConnectPoint point) {
+ Set<Link> portEgressLink = new HashSet<>();
+ DeviceId deviceId = point.deviceId();
+
+ portEgressLink.addAll(
+ //all egress links
+ egressLinkInfo.get(deviceId)
+ .stream()
+ .filter(l -> l.src().equals(point))
+ .collect(Collectors.toList()));
+
+ return portEgressLink;
+ }
+
+ private boolean matchAndAddFlowEntry(FlowEntry flowEntry,
+ TsLoopPacket pkt,
+ TsReturn<Boolean> isBigger) {
+ isBigger.setValue(false);
+
+ List<Criterion> criterionArray =
+ sortCriteria(flowEntry.selector().criteria());
+
+ for (Criterion criterion : criterionArray) {
+ // TODO - advance
+ switch (criterion.type()) {
+ case IN_PORT:
+ case ETH_SRC:
+ // At present, not support Ethernet mask (ONOS?)
+ case ETH_DST:
+ // At present, not support Ethernet mask (ONOS?)
+ case ETH_TYPE:
+ if (!matchAddExactly(pkt, criterion, isBigger)) {
+ return false;
+ }
+ break;
+ case VLAN_VID:
+ // At present, not support VLAN mask (ONOS?)
+ case VLAN_PCP:
+ if (!pkt.headerExists(ETH_TYPE) ||
+ !((EthTypeCriterion) pkt.getHeader(ETH_TYPE))
+ .ethType().equals(VLAN.ethType())) {
+ return false;
+ }
+ if (!matchAddExactly(pkt, criterion, isBigger)) {
+ return false;
+ }
+ break;
+ case IPV4_SRC:
+ case IPV4_DST:
+ if (!pkt.headerExists(ETH_TYPE) ||
+ !((EthTypeCriterion) pkt.getHeader(ETH_TYPE))
+ .ethType().equals(IPV4.ethType())) {
+ return false;
+ }
+ if (!matchAddIPV4(pkt, criterion, isBigger)) {
+ return false;
+ }
+ break;
+ case IP_PROTO:
+ if (!pkt.headerExists(ETH_TYPE) ||
+ !((EthTypeCriterion) pkt.getHeader(ETH_TYPE))
+ .ethType().equals(IPV4.ethType())) {
+ return false;
+ }
+ if (!matchAddExactly(pkt, criterion, isBigger)) {
+ return false;
+ }
+ break;
+ case IP_DSCP:
+ // TODO: 10/28/16 support IP_DSCP match field
+ break;
+ case IP_ECN:
+ // TODO: 10/28/16 support IP_DSCP match field
+ break;
+ case TCP_SRC:
+ case TCP_DST:
+ if (!pkt.headerExists(IP_PROTO) ||
+ IP_PROTO_TCP_TS !=
+ ((IPProtocolCriterion)
+ pkt.getHeader(IP_PROTO))
+ .protocol()
+ ) {
+ // has TCP match requirement, but can't afford TCP
+ return false;
+ }
+ // in this "for" loop
+ // avoid IP_PROTO locates after TCP_*
+ if (!matchAddExactly(pkt, criterion, isBigger)) {
+ return false;
+ }
+ break;
+ case UDP_SRC:
+ case UDP_DST:
+ if (!pkt.headerExists(IP_PROTO) ||
+ IP_PROTO_UDP_TS !=
+ ((IPProtocolCriterion)
+ pkt.getHeader(IP_PROTO))
+ .protocol()
+ ) {
+ // has UDP match requirement, but can't afford UDP
+ return false;
+ }
+ // in this "for" loop
+ // avoid IP_PROTO locates after UDP_*
+ if (!matchAddExactly(pkt, criterion, isBigger)) {
+ return false;
+ }
+ break;
+
+ default:
+ log.debug("{} can't be supported by OF1.0",
+ criterion.type());
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean matchAddExactly(TsLoopPacket pkt,
+ Criterion criterion,
+ TsReturn<Boolean> isBigger) {
+
+ if (pkt.headerExists(criterion.type())) {
+ if (!pkt.getHeader(criterion.type()).equals(criterion)) {
+ return false;
+ }
+
+ } else {
+ // TODO - check if it is IN_PORT or IN_PHY_PORT, should be strict
+ pkt.setHeader(criterion);
+ isBigger.setValue(true);
+ }
+
+ return true; // should put it here
+ }
+
+ // before invoking this, MUST insure EtherType is IPv4.
+ private boolean matchAddIPV4(TsLoopPacket pkt,
+ Criterion criterion,
+ TsReturn<Boolean> isBigger) {
+
+ if (pkt.headerExists(criterion.type())) {
+
+ IpPrefix ipFlow = ((IPCriterion) criterion).ip();
+ IpPrefix ipPkt =
+ ((IPCriterion) pkt.getHeader(criterion.type())).ip();
+
+ // attention - the order below is important
+ if (ipFlow.equals(ipPkt)) {
+ // shoot
+
+ } else if (ipFlow.contains(ipPkt)) {
+ // shoot, pkt is more exact than flowEntry
+
+ } else if (ipPkt.contains(ipFlow)) {
+ // pkt should be changed to be more exact
+ pkt.setHeader(criterion);
+ isBigger.setValue(true);
+ } else {
+ // match fail
+ return false;
+ }
+
+ } else {
+ // attention the order of criteria in "for" loop
+ pkt.setHeader(criterion);
+ isBigger.setValue(true);
+ }
+
+ return true;
+ }
+}
diff --git a/apps/network-troubleshoot/core/src/main/java/org/onosproject/fnl/impl/NetworkDiagnosticManager.java b/apps/network-troubleshoot/core/src/main/java/org/onosproject/fnl/impl/NetworkDiagnosticManager.java
new file mode 100644
index 0000000..628d8e6
--- /dev/null
+++ b/apps/network-troubleshoot/core/src/main/java/org/onosproject/fnl/impl/NetworkDiagnosticManager.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2015-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.fnl.impl;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Modified;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.onlab.util.Tools;
+import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.fnl.intf.NetworkAnomaly;
+import org.onosproject.fnl.intf.NetworkDiagnostic;
+import org.onosproject.fnl.intf.NetworkDiagnostic.Type;
+import org.onosproject.fnl.intf.NetworkDiagnosticService;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.host.HostService;
+import org.onosproject.net.link.LinkService;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Dictionary;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Default implementation of the Network Troubleshooting Core Service.
+ *
+ * It is simply modularized at present.
+ */
+@Component(immediate = true)
+@Service
+public class NetworkDiagnosticManager implements NetworkDiagnosticService {
+
+ /**
+ * Name of Network Troubleshooting Application.
+ */
+ public static final String NTS_APP_NAME =
+ "org.onosproject.FNL.Network-Troubleshoot";
+
+ private static final String PROPERTY_AUTO_REGISTER_DIAG =
+ "autoRegisterDefaultDiagnostics";
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ComponentConfigService cfgService;
+
+
+ // ------ service below is for auto register ------
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceService ds;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected HostService hs;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected FlowRuleService frs;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected LinkService ls;
+
+
+ @Property(name = PROPERTY_AUTO_REGISTER_DIAG, boolValue = true,
+ label = "Automatically register all of default diagnostic modules.")
+ private boolean autoRegister = true;
+
+
+ private ApplicationId appId;
+
+ private Map<Type, NetworkDiagnostic> diagnostics = new ConcurrentHashMap<>();
+
+
+ @Activate
+ protected void activate(ComponentContext context) {
+ appId = coreService.registerApplication(NTS_APP_NAME);
+
+ cfgService.registerProperties(getClass());
+ readConfiguration(context);
+
+ autoRegisterDiagnostics();
+
+ log.info("Started");
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ unregisterDiagnostics();
+ log.info("Stopped");
+ }
+
+ @Modified
+ protected void modified(ComponentContext context) {
+
+ readConfiguration(context);
+
+ // We should not register default diagnostics automatically
+ // in the Modify(), because that will erase the result of
+ // dynamic extension, run-time and custom diagnostics.
+ //
+ // And, using this modified() is to avoid deactivate-activate procedure.
+
+ log.info("Modified");
+ }
+
+ private void readConfiguration(ComponentContext context) {
+ Dictionary<?, ?> properties = context.getProperties();
+
+ Boolean autoRegisterEnabled =
+ Tools.isPropertyEnabled(properties, PROPERTY_AUTO_REGISTER_DIAG);
+ if (autoRegisterEnabled == null) {
+ log.warn("Auto Register is not configured, " +
+ "using current value of {}", autoRegister);
+ } else {
+ autoRegister = autoRegisterEnabled;
+ log.info("Configured. Auto Register is {}",
+ autoRegister ? "enabled" : "disabled");
+ }
+ }
+
+ private void autoRegisterDiagnostics() {
+ if (!autoRegister) {
+ return;
+ }
+
+ // TODO: 10/26/16 future new default diagnostics should be added here.
+ register(new DefaultCheckLoop(ds, hs, frs, ls));
+ }
+
+ private void unregisterDiagnostics() {
+ // TODO: 10/27/16 improve this for parallel.
+ diagnostics.clear();
+ }
+
+ @Override
+ public Set<NetworkAnomaly> findAnomalies() {
+ Set<NetworkAnomaly> allAnomalies = new HashSet<>();
+
+ diagnostics.forEach((type, diag) ->
+ allAnomalies.addAll(diag.findAnomalies()));
+
+ return allAnomalies;
+ }
+
+ @Override
+ public Set<NetworkAnomaly> findAnomalies(Type type) {
+ checkNotNull(type, "NetworkAnomaly Type cannot be null");
+
+ Set<NetworkAnomaly> anomalies = new HashSet<>();
+
+ NetworkDiagnostic diag = diagnostics.get(type);
+ if (diag == null) {
+ log.warn("no anomalies of type {} found", type);
+ return anomalies;
+ }
+
+ anomalies.addAll(diag.findAnomalies());
+
+ return anomalies;
+ }
+
+ @Override
+ public void register(NetworkDiagnostic diag) {
+ checkNotNull(diag, "Diagnostic cannot be null");
+
+ NetworkDiagnostic oldDiag = diagnostics.put(diag.type(), diag);
+
+ if (oldDiag != null) {
+ log.warn("previous diagnostic {} is replaced by {},",
+ oldDiag.getClass(), diag.getClass());
+ } else {
+ log.info("Register {} type module: {}",
+ diag.type(), diag.getClass().getName());
+ }
+ }
+
+ @Override
+ public boolean unregister(NetworkDiagnostic diag) {
+ checkNotNull(diag, "Diagnostic cannot be null");
+
+ return diagnostics.remove(diag.type(), diag);
+ }
+}
diff --git a/apps/network-troubleshoot/core/src/main/java/org/onosproject/fnl/impl/package-info.java b/apps/network-troubleshoot/core/src/main/java/org/onosproject/fnl/impl/package-info.java
new file mode 100644
index 0000000..c02b757
--- /dev/null
+++ b/apps/network-troubleshoot/core/src/main/java/org/onosproject/fnl/impl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * The Implement of troubleshoot Core and specific checking modules.
+ */
+package org.onosproject.fnl.impl;
diff --git a/apps/network-troubleshoot/core/src/test/java/org/onosproject/fnl/impl/DefaultCheckLoopTest.java b/apps/network-troubleshoot/core/src/test/java/org/onosproject/fnl/impl/DefaultCheckLoopTest.java
new file mode 100644
index 0000000..3c72a09
--- /dev/null
+++ b/apps/network-troubleshoot/core/src/test/java/org/onosproject/fnl/impl/DefaultCheckLoopTest.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright 2015-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.fnl.impl;
+
+import com.google.common.collect.ImmutableSet;
+import org.easymock.EasyMock;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.ChassisId;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.fnl.intf.NetworkAnomaly;
+import org.onosproject.fnl.intf.NetworkDiagnostic;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultDevice;
+import org.onosproject.net.DefaultHost;
+import org.onosproject.net.DefaultLink;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Host;
+import org.onosproject.net.HostId;
+import org.onosproject.net.HostLocation;
+import org.onosproject.net.Link;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.DefaultFlowEntry;
+import org.onosproject.net.flow.DefaultFlowRule;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.host.HostService;
+import org.onosproject.net.link.LinkService;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
+import static org.onlab.packet.EthType.EtherType.IPV4;
+import static org.onlab.packet.TpPort.tpPort;
+import static org.onosproject.net.DefaultAnnotations.EMPTY;
+import static org.onosproject.net.Link.State.ACTIVE;
+import static org.onosproject.net.Link.Type.DIRECT;
+import static org.onosproject.net.PortNumber.portNumber;
+import static org.onosproject.net.flow.FlowEntry.FlowEntryState.ADDED;
+import static org.onosproject.net.provider.ProviderId.NONE;
+
+/**
+ * Unit Tests for DefaultCheckLoop class.
+ */
+public class DefaultCheckLoopTest {
+
+ private NetworkDiagnostic defaultCheckLoop;
+
+ private DeviceService ds;
+ private HostService hs;
+ private FlowRuleService frs;
+ private LinkService ls;
+
+ private List<Device> devices;
+ private List<Host> hosts;
+ private Map<DeviceId, Set<Link>> egressLinks;
+ private Map<DeviceId, List<FlowEntry>> flowEntries;
+
+
+ private static final DeviceId DEVICE_ID_A = DeviceId.deviceId("of:000000000000000A");
+ private static final DeviceId DEVICE_ID_B = DeviceId.deviceId("of:000000000000000B");
+ private static final DeviceId DEVICE_ID_C = DeviceId.deviceId("of:000000000000000C");
+ private static final DeviceId DEVICE_ID_D = DeviceId.deviceId("of:000000000000000D");
+ private static final DeviceId DEVICE_ID_E = DeviceId.deviceId("of:E000000000000000");
+ private static final DeviceId DEVICE_ID_F = DeviceId.deviceId("of:F000000000000000");
+ private static final DeviceId DEVICE_ID_G = DeviceId.deviceId("of:A000000000000000");
+
+ private static final String HOSTID_EXAMPLE = "12:34:56:78:9A:BC/1355";
+ private static final long FLOWRULE_COOKIE_EXAMPLE = 708;
+ private static final int FLOWRULE_PRIORITY_EXAMPLE = 738;
+
+
+ @Before
+ public void setUp() {
+ ds = EasyMock.createMock(DeviceService.class);
+ hs = EasyMock.createMock(HostService.class);
+ frs = EasyMock.createMock(FlowRuleService.class);
+ ls = EasyMock.createMock(LinkService.class);
+
+ defaultCheckLoop = new DefaultCheckLoop(ds, hs, frs, ls);
+ }
+
+ @After
+ public void tearDown() {
+ // do nothing
+ }
+
+ @Test
+ public void testFindLoops() {
+ produceTopoDevices();
+ produceTopoHosts();
+ produceTopoLinks();
+ produceFlowEntries();
+
+ initMock();
+
+ Set<NetworkAnomaly> loops = defaultCheckLoop.findAnomalies();
+ assertThat(loops, hasSize(1));
+ }
+
+ private void initMock() {
+ expect(ds.getDevices()).andReturn(devices).anyTimes();
+ replay(ds);
+
+ expect(hs.getHosts()).andReturn(hosts).anyTimes();
+ replay(hs);
+
+ // --- init flow rule service ---
+
+ expect(frs.getFlowEntries(DEVICE_ID_A))
+ .andReturn(flowEntries.get(DEVICE_ID_A)).anyTimes();
+ expect(frs.getFlowEntries(DEVICE_ID_B))
+ .andReturn(flowEntries.get(DEVICE_ID_B)).anyTimes();
+ expect(frs.getFlowEntries(DEVICE_ID_C))
+ .andReturn(flowEntries.get(DEVICE_ID_C)).anyTimes();
+ expect(frs.getFlowEntries(DEVICE_ID_D))
+ .andReturn(flowEntries.get(DEVICE_ID_D)).anyTimes();
+ expect(frs.getFlowEntries(DEVICE_ID_E))
+ .andReturn(flowEntries.get(DEVICE_ID_E)).anyTimes();
+ expect(frs.getFlowEntries(DEVICE_ID_F))
+ .andReturn(flowEntries.get(DEVICE_ID_F)).anyTimes();
+ expect(frs.getFlowEntries(DEVICE_ID_G))
+ .andReturn(flowEntries.get(DEVICE_ID_G)).anyTimes();
+ replay(frs);
+
+ // --- init link service ---
+
+ expect(ls.getDeviceEgressLinks(DEVICE_ID_A))
+ .andReturn(egressLinks.get(DEVICE_ID_A)).anyTimes();
+ expect(ls.getDeviceEgressLinks(DEVICE_ID_B))
+ .andReturn(egressLinks.get(DEVICE_ID_B)).anyTimes();
+ expect(ls.getDeviceEgressLinks(DEVICE_ID_C))
+ .andReturn(egressLinks.get(DEVICE_ID_C)).anyTimes();
+ expect(ls.getDeviceEgressLinks(DEVICE_ID_D))
+ .andReturn(egressLinks.get(DEVICE_ID_D)).anyTimes();
+ expect(ls.getDeviceEgressLinks(DEVICE_ID_E))
+ .andReturn(egressLinks.get(DEVICE_ID_E)).anyTimes();
+ expect(ls.getDeviceEgressLinks(DEVICE_ID_F))
+ .andReturn(egressLinks.get(DEVICE_ID_F)).anyTimes();
+ expect(ls.getDeviceEgressLinks(DEVICE_ID_G))
+ .andReturn(egressLinks.get(DEVICE_ID_G)).anyTimes();
+ replay(ls);
+ }
+
+ private void produceTopoDevices() {
+ devices = new ArrayList<>();
+ devices.add(produceOneDevice(DEVICE_ID_A));
+ devices.add(produceOneDevice(DEVICE_ID_B));
+ devices.add(produceOneDevice(DEVICE_ID_C));
+ devices.add(produceOneDevice(DEVICE_ID_D));
+ devices.add(produceOneDevice(DEVICE_ID_E));
+ devices.add(produceOneDevice(DEVICE_ID_F));
+ devices.add(produceOneDevice(DEVICE_ID_G));
+ }
+
+ private Device produceOneDevice(DeviceId dpid) {
+ return new DefaultDevice(NONE, dpid,
+ Device.Type.SWITCH, "", "", "", "", new ChassisId(),
+ EMPTY);
+ }
+
+ private void produceTopoHosts() {
+ hosts = new ArrayList<>();
+ hosts.add(produceOneHost(DEVICE_ID_A, 3));
+ hosts.add(produceOneHost(DEVICE_ID_B, 3));
+ hosts.add(produceOneHost(DEVICE_ID_D, 3));
+ hosts.add(produceOneHost(DEVICE_ID_F, 2));
+ hosts.add(produceOneHost(DEVICE_ID_F, 3));
+ hosts.add(produceOneHost(DEVICE_ID_G, 1));
+ hosts.add(produceOneHost(DEVICE_ID_G, 3));
+ }
+
+ private Host produceOneHost(DeviceId dpid, int port) {
+ return new DefaultHost(NONE, HostId.hostId(HOSTID_EXAMPLE),
+ MacAddress.valueOf(0), VlanId.vlanId(),
+ new HostLocation(dpid, portNumber(port), 1),
+ ImmutableSet.of(), EMPTY);
+ }
+
+ private void produceTopoLinks() {
+ egressLinks = new HashMap<>();
+
+ Set<Link> el = new HashSet<>();
+ el.add(produceOneEgressLink(DEVICE_ID_A, 1, DEVICE_ID_B, 1));
+ el.add(produceOneEgressLink(DEVICE_ID_A, 2, DEVICE_ID_D, 2));
+ egressLinks.put(DEVICE_ID_A, el);
+
+ el = new HashSet<>();
+ el.add(produceOneEgressLink(DEVICE_ID_B, 1, DEVICE_ID_A, 1));
+ el.add(produceOneEgressLink(DEVICE_ID_B, 2, DEVICE_ID_C, 2));
+ egressLinks.put(DEVICE_ID_B, el);
+
+ el = new HashSet<>();
+ el.add(produceOneEgressLink(DEVICE_ID_C, 1, DEVICE_ID_D, 1));
+ el.add(produceOneEgressLink(DEVICE_ID_C, 2, DEVICE_ID_B, 2));
+ el.add(produceOneEgressLink(DEVICE_ID_C, 3, DEVICE_ID_E, 3));
+ egressLinks.put(DEVICE_ID_C, el);
+
+ el = new HashSet<>();
+ el.add(produceOneEgressLink(DEVICE_ID_D, 1, DEVICE_ID_C, 1));
+ el.add(produceOneEgressLink(DEVICE_ID_D, 2, DEVICE_ID_A, 2));
+ egressLinks.put(DEVICE_ID_D, el);
+
+ el = new HashSet<>();
+ el.add(produceOneEgressLink(DEVICE_ID_E, 1, DEVICE_ID_F, 1));
+ el.add(produceOneEgressLink(DEVICE_ID_E, 2, DEVICE_ID_G, 2));
+ el.add(produceOneEgressLink(DEVICE_ID_E, 3, DEVICE_ID_C, 3));
+ egressLinks.put(DEVICE_ID_E, el);
+
+ el = new HashSet<>();
+ el.add(produceOneEgressLink(DEVICE_ID_F, 1, DEVICE_ID_E, 1));
+ egressLinks.put(DEVICE_ID_F, el);
+
+ el = new HashSet<>();
+ el.add(produceOneEgressLink(DEVICE_ID_G, 2, DEVICE_ID_E, 2));
+ egressLinks.put(DEVICE_ID_G, el);
+ }
+
+ private Link produceOneEgressLink(DeviceId srcId, int srcPort, DeviceId dstId, int dstPort) {
+ return DefaultLink.builder()
+ .providerId(NONE)
+ .src(new ConnectPoint(srcId, portNumber(srcPort)))
+ .dst(new ConnectPoint(dstId, portNumber(dstPort)))
+ .type(DIRECT)
+ .state(ACTIVE)
+ .build();
+ }
+
+ private void produceFlowEntries() {
+ flowEntries = new HashMap<>();
+
+ List<FlowEntry> fe = new ArrayList<>();
+ fe.add(produceOneFlowEntry(DEVICE_ID_A, 2));
+ flowEntries.put(DEVICE_ID_A, fe);
+
+ fe = new ArrayList<>();
+ fe.add(produceOneFlowEntry(DEVICE_ID_D, 1));
+ flowEntries.put(DEVICE_ID_D, fe);
+
+ fe = new ArrayList<>();
+ fe.add(produceOneFlowEntry(DEVICE_ID_C, 2));
+ flowEntries.put(DEVICE_ID_C, fe);
+
+ fe = new ArrayList<>();
+ fe.add(produceOneFlowEntry(DEVICE_ID_B, 1));
+ flowEntries.put(DEVICE_ID_B, fe);
+
+ flowEntries.put(DEVICE_ID_E, new ArrayList<>());
+ flowEntries.put(DEVICE_ID_F, new ArrayList<>());
+ flowEntries.put(DEVICE_ID_G, new ArrayList<>());
+
+ }
+
+ private FlowEntry produceOneFlowEntry(DeviceId dpid, int outPort) {
+
+ TrafficSelector.Builder sb = DefaultTrafficSelector.builder()
+ .matchEthType(IPV4.ethType().toShort())
+ .matchIPDst(IpPrefix.valueOf("10.0.0.0/8"))
+ .matchIPProtocol(IPv4.PROTOCOL_TCP)
+ .matchTcpDst(tpPort(22));
+
+ TrafficTreatment.Builder tb = DefaultTrafficTreatment.builder()
+ .setOutput(portNumber(outPort));
+
+ return new DefaultFlowEntry(DefaultFlowRule.builder()
+ .withPriority(FLOWRULE_PRIORITY_EXAMPLE).forDevice(dpid).forTable(0)
+ .withCookie(FLOWRULE_COOKIE_EXAMPLE)
+ .withSelector(sb.build()).withTreatment(tb.build())
+ .makePermanent().build(), ADDED);
+ }
+}
diff --git a/apps/network-troubleshoot/pom.xml b/apps/network-troubleshoot/pom.xml
new file mode 100644
index 0000000..6ffd16b
--- /dev/null
+++ b/apps/network-troubleshoot/pom.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2015-present Open Networking Laboratory
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-apps</artifactId>
+ <version>1.10.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>onos-network-troubleshoot</artifactId>
+ <packaging>pom</packaging>
+
+ <description>ONOS Network TroubleShoot SubSystem</description>
+
+ <modules>
+ <module>api</module>
+ <module>cli</module>
+ <module>core</module>
+ <module>app</module>
+ </modules>
+
+</project>