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