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/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();
+    }
+}