diff --git a/apps/network-troubleshoot/BUCK b/apps/network-troubleshoot/BUCK new file mode 100644 index 0000000000..612bff698d --- /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 0000000000..b6fdc640be --- /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 0000000000..0d5cfcc3fa --- /dev/null +++ b/apps/network-troubleshoot/api/pom.xml @@ -0,0 +1,45 @@ + + + + + 4.0.0 + + + org.onosproject + onos-network-troubleshoot + 1.10.0-SNAPSHOT + + + onos-network-troubleshoot-api + bundle + + ONOS Network TroubleShoot SubSystem API + + + + junit + junit + + + org.onosproject + onlab-junit + test + + + 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 0000000000..0a0d796061 --- /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 sortFlowTable(Iterable flowEntries) { + + List 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 sortCriteria(Set criterionSet) { + + List 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 0000000000..cb7d4550ec --- /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 match; + private Stack pathFlow; + // when Upgrade, check to MAKE SURE it include just Link but not EdgeLink + private Stack 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 m = newOne.match; + + for (Map.Entry 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 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 criteria, + TsReturn 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 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 0000000000..f0f56d559d --- /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 the class of expected return value + */ +public final class TsReturn { + 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 0000000000..aca2d7b3bf --- /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 0000000000..aa754a7246 --- /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 0000000000..55122f1b99 --- /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 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 0000000000..a3086ba5bf --- /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 findAnomalies(); + + /** + * Checks for and returns the specific kind of network anomalies. + *

+ * 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. + *

+ * 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 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 0000000000..4f7a3b8c81 --- /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 0000000000..3d7cacd823 --- /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 bool = new TsReturn<>(); + assertFalse(bool.isPresent()); + + bool.setValue(NEW_BOOLEAN_EXAMPLE); + assertTrue(bool.isPresent()); + } + + @Test + public void testRoleReturnBoolean() { + TsReturn 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 = 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 0000000000..a81cf6d6de --- /dev/null +++ b/apps/network-troubleshoot/app/app.xml @@ -0,0 +1,30 @@ + + + + + ${project.description} + + mvn:${project.groupId}/onos-network-troubleshoot-api/${project.version} + mvn:${project.groupId}/onos-network-troubleshoot-core/${project.version} + mvn:${project.groupId}/onos-network-troubleshoot-cli/${project.version} + + diff --git a/apps/network-troubleshoot/app/features.xml b/apps/network-troubleshoot/app/features.xml new file mode 100644 index 0000000000..266db1eea3 --- /dev/null +++ b/apps/network-troubleshoot/app/features.xml @@ -0,0 +1,29 @@ + + + + + + + mvn:${project.groupId}/onos-network-troubleshoot-api/${project.version} + mvn:${project.groupId}/onos-network-troubleshoot-core/${project.version} + mvn:${project.groupId}/onos-network-troubleshoot-cli/${project.version} + + + + diff --git a/apps/network-troubleshoot/app/pom.xml b/apps/network-troubleshoot/app/pom.xml new file mode 100644 index 0000000000..90bffa3a67 --- /dev/null +++ b/apps/network-troubleshoot/app/pom.xml @@ -0,0 +1,56 @@ + + + + + 4.0.0 + + + org.onosproject + onos-network-troubleshoot + 1.10.0-SNAPSHOT + + + onos-network-troubleshoot-app + pom + + ONOS Network TroubleShoot SubSystem + + + + + org.onosproject + onos-network-troubleshoot-api + ${project.version} + + + + org.onosproject + onos-network-troubleshoot-cli + ${project.version} + + + + org.onosproject + onos-network-troubleshoot-core + ${project.version} + + + + + diff --git a/apps/network-troubleshoot/cli/BUCK b/apps/network-troubleshoot/cli/BUCK new file mode 100644 index 0000000000..8f3fe1ee5c --- /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 0000000000..e46c143b34 --- /dev/null +++ b/apps/network-troubleshoot/cli/pom.xml @@ -0,0 +1,56 @@ + + + + + 4.0.0 + + + org.onosproject + onos-network-troubleshoot + 1.10.0-SNAPSHOT + + + onos-network-troubleshoot-cli + bundle + + ONOS Network TroubleShoot SubSystem + + + + + org.onosproject + onos-network-troubleshoot-api + ${project.version} + + + + + org.apache.karaf.shell + org.apache.karaf.shell.console + + + + org.onosproject + onos-cli + ${project.version} + + + + + 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 0000000000..a6c60c7cde --- /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 0000000000..bb928abedf --- /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 0000000000..1ef3790dfd --- /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 0000000000..01c6028b0e --- /dev/null +++ b/apps/network-troubleshoot/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + diff --git a/apps/network-troubleshoot/core/BUCK b/apps/network-troubleshoot/core/BUCK new file mode 100644 index 0000000000..1174519cad --- /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 0000000000..43f02815ba --- /dev/null +++ b/apps/network-troubleshoot/core/pom.xml @@ -0,0 +1,76 @@ + + + + + 4.0.0 + + + org.onosproject + onos-network-troubleshoot + 1.10.0-SNAPSHOT + + + onos-network-troubleshoot-core + bundle + + ONOS Network TroubleShoot SubSystem + + + + + org.onosproject + onos-network-troubleshoot-api + ${project.version} + + + + + org.osgi + org.osgi.compendium + + + + + junit + junit + + + org.onosproject + onlab-junit + test + + + org.hamcrest + hamcrest-core + test + + + org.hamcrest + hamcrest-library + test + + + org.easymock + easymock + test + + + + + 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 0000000000..363468376c --- /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 deviceInfo; + private Map> flowEntryInfo; + private Set accessDevices; + + //conventionally used by tsGetEgressLinks() + private Map> egressLinkInfo; + + //Two below are hot data in checking process. + private Set loops; + private Set 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 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 findLoop() { + + getNetworkSnapshot(); + + loops = new HashSet<>(); + excludeDeviceId = new HashSet<>(); + + for (Device device : accessDevices) { + if (excludeDeviceId.contains(device.id())) { + continue; + } + + + List availableFlowEntries = new ArrayList<>(); + + flowEntryInfo.get(device.id()).forEach(flowEntry -> { + if (flowEntry.state() == ADDED) { + availableFlowEntries.add(flowEntry); + } + }); + + List sortedFlowEntries = sortFlowTable(availableFlowEntries); + + + for (FlowEntry flow : sortedFlowEntries) { + + TsLoopPacket pkt = + matchBuilder(flow.selector().criteria(), null); + + pkt.pushPathFlow(flow); + + List 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 availableFlowEntries = new ArrayList<>(); + + flowEntryInfo.get(deviceId).forEach(flowEntry -> { + if (flowEntry.state() == ADDED) { + availableFlowEntries.add(flowEntry); + } + }); + + List sortedFlowEntries = sortFlowTable(availableFlowEntries); + + + for (FlowEntry flowEntry : sortedFlowEntries) { + TsReturn 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 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 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 tsGetEgressLinks(ConnectPoint point) { + Set 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 isBigger) { + isBigger.setValue(false); + + List 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 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 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 0000000000..628d8e6619 --- /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 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 findAnomalies() { + Set allAnomalies = new HashSet<>(); + + diagnostics.forEach((type, diag) -> + allAnomalies.addAll(diag.findAnomalies())); + + return allAnomalies; + } + + @Override + public Set findAnomalies(Type type) { + checkNotNull(type, "NetworkAnomaly Type cannot be null"); + + Set 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 0000000000..c02b75786c --- /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 0000000000..3c72a09b5b --- /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 devices; + private List hosts; + private Map> egressLinks; + private Map> 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 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 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 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 0000000000..6ffd16b8c4 --- /dev/null +++ b/apps/network-troubleshoot/pom.xml @@ -0,0 +1,41 @@ + + + + + 4.0.0 + + + org.onosproject + onos-apps + 1.10.0-SNAPSHOT + + + onos-network-troubleshoot + pom + + ONOS Network TroubleShoot SubSystem + + + api + cli + core + app + + + diff --git a/apps/pom.xml b/apps/pom.xml index 911920d38a..ab56021d8d 100644 --- a/apps/pom.xml +++ b/apps/pom.xml @@ -89,6 +89,7 @@ intentsync mappingmanagement yang + network-troubleshoot diff --git a/modules.defs b/modules.defs index 5e4a315e28..73b80c40b5 100644 --- a/modules.defs +++ b/modules.defs @@ -190,7 +190,8 @@ ONOS_APPS = [ '//apps/flowspec-api:onos-apps-flowspec-api-oar', '//apps/yang:onos-apps-yang-oar', '//apps/yang-gui:onos-apps-yang-gui-oar', - '//apps/cord-support:onos-apps-cord-support-oar' + '//apps/cord-support:onos-apps-cord-support-oar', + '//apps/network-troubleshoot:onos-apps-network-troubleshoot-oar' ] APP_JARS = [