/*
* Copyright (c) 2013 Big Switch Networks, Inc.
*
* Licensed under the Eclipse Public License, Version 1.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.eclipse.org/legal/epl-v10.html
*
* 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.sdnplatform.flowcache;
import java.util.ArrayList;
import java.util.ListIterator;
import org.openflow.protocol.OFMatch;
import org.openflow.protocol.OFMatchWithSwDpid;
import org.openflow.util.HexString;
import org.sdnplatform.flowcache.BetterFlowCache.FCOper;
import org.sdnplatform.packet.Ethernet;
import org.sdnplatform.packet.IPv4;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The Class FlowCacheObj. This is a helper class to BetterFlowCache class that
* performs the lower level flow lookup, insertion, activation, deactivation
* functions.
*/
public class FlowCacheObj {
protected static Logger logger =LoggerFactory.getLogger(FlowCacheObj.class);
/** Flow is active */
public static final byte FCStateACTIVE = 1;
/**
* Flow is removed but it is still kept in flow cache for performance
* reason
*/
public static final byte FCStateINACTIVE = 2;
/** Flow entry is created but not used yet */
private static final byte FCStateUNUSED = 3;
/** The flow entry match permits traffic */
public static final byte FCActionPERMIT = 1;
/** The flow entry match drops traffic */
public static final byte FCActionDENY = 2;
/** A flow is dampened if it was inserted in the flow cache less than
* DAMPEN_TIMEOUT_NS ago. Flow mods for dampened flows are not pushed to
* the switch flow table.
* all time is memsured in millisecond now.
*/
private static final long DAMPEN_TIMEOUT_NS = 1500000000; // 1.5 seconds
//************************
// Commonly used wildcards
//************************
/* Some commonly used wildcards used for faster processing, for example in
* comparison operations
* Wildcard is a 22-bit field as per open-flow spec.
*/
/* The Match all wildcard.
*/
final public static int WILD_ALL = 0x3FFFFF;
/* The following wildcard is used in core switches and on internal ports
* This wildcards all fields except VLAN and Dest MAC address.
*/
final private static int WILD_MATCH_VLAN_DLSADR = 0x3FFFA0;
/* The wild card below is commonly used for edge ports
* Input-port, vlan, src and dest macs are NOT wild-carded.
*/
final public static int WILD_MATCH_INP_VLAN_DLADRS = 0x3FFFF0;
/* The wild card below is commonly used when acl is "permit ip any any"
* Input-port, vlan, src and dest macs, new protocol are NOT wild-carded.
*/
final public static int WILD_MATCH_INP_VLAN_DLADRS_ET = 0x3FFFE0;
/* Input-port, vlan, src and dest macs etype, proto, dstprt are NOT
* wild-carded.
*/
final private static int WILD_MATCH_INP_VLAN_DLADRS_ET_PROTO_DP = 0x3FFF40;
/* Input-port, vlan, src and dest macs etype, proto are NOT wild-carded.
*/
final private static int WILD_MATCH_INP_VLAN_DLADRS_ET_PROTO = 0x3FFFC0;
/* The one below is used at switches that need L3_MATCH
* Input-port, vlan, src and dest macs, ether-type and L3 addresses are
* NOT wild-carded.
*/
final private static int WILD_MATCH_INP_VLAN_DLADRS_ETYP_NWADRS = 0x3000E0;
final private static int WILD_PCP_TOS_L4PSPRT = 0x300060;
final private static int WILD_PCP_TOS_L4PDPRT = 0x3000A0;
final private static int WILD_PCP_L3SADR_TOS_L4SPRT = 0x303F60;
final private static int WILD_PCP_L3DADR_TOS_L4DPRT = 0x3FC0A0;
final private static int WILD_PCP_L3PROT_TOS = 0x300020;
final private static int WILD_NONE = 0x0;
//***************************
// FlowCacheObj class members
//***************************
/**
* Most of the flows are expected to have one fce and the fce member would
* be used to store that flow entry. In some cases however there would be
* multiple flow entries between a source-destination pair and in those
* cases the fceList would be used to store the 2nd and subsequent flows.
* If there is only one flow entry then fceList member would be set to null.
* Reason for this approach is not to user 12 bytes of arraylist overhead
* (24 bytes on a 64-bit machine) for every flow in the flow cache when
* there is only one flow mod for a flow.
*
* One example where there could be multiple flow specs between a source
* and a destination is when there is an ACL that permits specific
* transport layer ports.
*/
public FCEntry fce;
public ArrayList<FCEntry> fceList;
/* If the entry is active then it is the time stamp of the latest fce
* installation time in this FlowCacheObject
*/
public long installTimeNs;
public long cookie;
/**
* Create an instance of flow cache object.
*/
protected FlowCacheObj() {
fce = new FCEntry();
fceList = null;
}
/**
* This class is used to represent a flow entry as it is stored in the flow
* cache. Src MAC, Dest MAC and VLAN are used as hash keys hence they are
* not stored in the FCEntry object.
*/
public class FCEntry { /* 32-bytes (four 8-bytes) */
protected long srcSwitchDpid;
protected short srcL4Port;
protected short destL4Port;
protected short inputPort;
protected short etherType;
protected int srcIpAddr;
protected int destIpAddr;
protected int wildcards;
protected short ofPri; /* openflow flow-mod priority */
protected byte protocol; /* Network (L3) protocol */
protected byte state; /* See FCState* above */
protected byte action; /* See FCAction* above */
protected byte nwTos; /* Network TOS field */
protected byte pcp; /* vlan priority code point */
/* The scan count is incremented every scan period. It is reset when
* a response from the switch flow table query hits this entry. It is
* also reset when the entry gets deactivated. If this entry is active
* and the scan count is greater than a threshold then the entry is
* deleted. This handles the case when the flow mod removal message
* was not sent or got lost.
*/
protected byte scanCnt; /* incremented every scan period */
/**
* Create an instance of flow-cache entry object
* @param ofmWithSwDpid flow match structure
* @param priority flow-mod priority
* @param action PERMIT or DENY action
*/
protected FCEntry(OFMatchWithSwDpid ofmWithSwDpid, short priority, byte action) {
this.fcEntryInit(ofmWithSwDpid, priority, action);
}
protected FCEntry() {
state = FCStateUNUSED; /* free */
scanCnt = 0;
}
private void fcEntryInit(OFMatchWithSwDpid ofmWithSwDpid, short priority, byte action) {
srcSwitchDpid = ofmWithSwDpid.getSwitchDataPathId();
srcL4Port = ofmWithSwDpid.getOfMatch().getTransportSource();
destL4Port = ofmWithSwDpid.getOfMatch().getTransportDestination();
inputPort = ofmWithSwDpid.getOfMatch().getInputPort();
etherType = ofmWithSwDpid.getOfMatch().getDataLayerType();
srcIpAddr = ofmWithSwDpid.getOfMatch().getNetworkSource();
destIpAddr = ofmWithSwDpid.getOfMatch().getNetworkDestination();
wildcards = ofmWithSwDpid.getOfMatch().getWildcards();
this.ofPri = priority;
protocol = ofmWithSwDpid.getOfMatch().getNetworkProtocol();
this.state = FCStateACTIVE;
this.action = action;
nwTos = ofmWithSwDpid.getOfMatch().getNetworkTypeOfService();
pcp = ofmWithSwDpid.getOfMatch().getDataLayerVirtualLanPriorityCodePoint();
scanCnt = 0;
}
/* Returns a OFMatch object populated from this fce and supplied
* parameters
*/
protected void toOFMatchWithSwDpid(OFMatchWithSwDpid ofmWithSwDpid,
short vlan, long srcMac, long dstMac) {
/* Populate the OFMatch object */
ofmWithSwDpid.getOfMatch().setDataLayerSource(Ethernet.toByteArray(srcMac));
ofmWithSwDpid.getOfMatch().setDataLayerDestination(Ethernet.toByteArray(dstMac));
ofmWithSwDpid.getOfMatch().setDataLayerVirtualLan(vlan);
ofmWithSwDpid.getOfMatch().setTransportSource(srcL4Port);
ofmWithSwDpid.getOfMatch().setTransportDestination(destL4Port);
ofmWithSwDpid.getOfMatch().setInputPort(inputPort);
ofmWithSwDpid.getOfMatch().setDataLayerType(etherType);
ofmWithSwDpid.getOfMatch().setNetworkSource(srcIpAddr);
ofmWithSwDpid.getOfMatch().setNetworkDestination(destIpAddr);
ofmWithSwDpid.getOfMatch().setWildcards(wildcards);
ofmWithSwDpid.getOfMatch().setNetworkProtocol(protocol);
ofmWithSwDpid.getOfMatch().setNetworkTypeOfService(nwTos);
ofmWithSwDpid.getOfMatch().setDataLayerVirtualLanPriorityCodePoint(pcp);
ofmWithSwDpid.setSwitchDataPathId(srcSwitchDpid);
}
/* Returns a OFFlowMod object populated from this fce and supplied
* parameters
*/
protected void toOFMatchReconcile(OFMatchReconcile ofmRc,
short vlan, long srcMac, long dstMac) {
/* Populate the OFMatch object */
OFMatchWithSwDpid ofmWithSwDpid = ofmRc.ofmWithSwDpid;
ofmWithSwDpid.getOfMatch().setDataLayerSource(Ethernet.toByteArray(srcMac));
ofmWithSwDpid.getOfMatch().setDataLayerDestination(Ethernet.toByteArray(dstMac));
ofmWithSwDpid.getOfMatch().setDataLayerVirtualLan(vlan);
ofmWithSwDpid.getOfMatch().setTransportSource(srcL4Port);
ofmWithSwDpid.getOfMatch().setTransportDestination(destL4Port);
ofmWithSwDpid.getOfMatch().setInputPort(inputPort);
ofmWithSwDpid.getOfMatch().setDataLayerType(etherType);
ofmWithSwDpid.getOfMatch().setNetworkSource(srcIpAddr);
ofmWithSwDpid.getOfMatch().setNetworkDestination(destIpAddr);
ofmWithSwDpid.getOfMatch().setWildcards(wildcards);
ofmWithSwDpid.getOfMatch().setNetworkProtocol(protocol);
ofmWithSwDpid.getOfMatch().setNetworkTypeOfService(nwTos);
ofmWithSwDpid.getOfMatch().setDataLayerVirtualLanPriorityCodePoint(pcp);
ofmWithSwDpid.setSwitchDataPathId(srcSwitchDpid);
ofmRc.rcAction = OFMatchReconcile.ReconcileAction.NO_CHANGE;
ofmRc.action = action;
}
/**
* Compares if a given object is equal to this FCEntry object
* @param obj object to compare with
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof FCEntry)) {
return false;
}
FCEntry o = (FCEntry) obj;
if (srcSwitchDpid != o.srcSwitchDpid) {
return false;
}
/* First check the wildcards since that determines how rest of the
* OFMatch fields are relevant for equals comparison.
*/
if (wildcards != o.wildcards) {
return false;
}
switch (wildcards) {
case WILD_MATCH_INP_VLAN_DLADRS: /* Edge ports */
if ((srcSwitchDpid == o.srcSwitchDpid) &&
(inputPort == o.inputPort) &&
(ofPri == o.ofPri)) {
return true;
}
break;
case WILD_MATCH_INP_VLAN_DLADRS_ET: /* permit ip any any */
if ((srcSwitchDpid == o.srcSwitchDpid) &&
(inputPort == o.inputPort) &&
(etherType == o.etherType) &&
(ofPri == o.ofPri)) {
return true;
}
break;
case WILD_MATCH_INP_VLAN_DLADRS_ET_PROTO_DP:
if ((srcSwitchDpid == o.srcSwitchDpid) &&
(destL4Port == o.destL4Port) &&
(inputPort == o.inputPort) &&
(etherType == o.etherType) &&
(ofPri == o.ofPri) &&
(protocol == o.protocol)) {
return true;
}
break;
case WILD_MATCH_INP_VLAN_DLADRS_ET_PROTO:
if ((srcSwitchDpid == o.srcSwitchDpid) &&
(inputPort == o.inputPort) &&
(etherType == o.etherType) &&
(ofPri == o.ofPri) &&
(protocol == o.protocol)) {
return true;
}
break;
case WILD_MATCH_VLAN_DLSADR: /* Core or internal ports */
if (srcSwitchDpid == o.srcSwitchDpid) {
return true;
}
break;
case WILD_MATCH_INP_VLAN_DLADRS_ETYP_NWADRS:
if ((srcSwitchDpid == o.srcSwitchDpid) &&
(inputPort == o.inputPort) &&
(etherType == o.etherType) &&
(srcIpAddr == o.srcIpAddr) &&
(destIpAddr == o.destIpAddr) &&
(ofPri == o.ofPri)) {
return true;
}
break;
case WILD_PCP_TOS_L4PSPRT:
if ((srcSwitchDpid == o.srcSwitchDpid) &&
(destL4Port == o.destL4Port) &&
(inputPort == o.inputPort) &&
(etherType == o.etherType) &&
(srcIpAddr == o.srcIpAddr) &&
(destIpAddr == o.destIpAddr) &&
(ofPri == o.ofPri) &&
(protocol == o.protocol)) {
return true;
}
break;
case WILD_PCP_TOS_L4PDPRT:
if ((srcSwitchDpid == o.srcSwitchDpid) &&
(srcL4Port == o.srcL4Port) &&
(inputPort == o.inputPort) &&
(etherType == o.etherType) &&
(srcIpAddr == o.srcIpAddr) &&
(destIpAddr == o.destIpAddr) &&
(ofPri == o.ofPri) &&
(protocol == o.protocol)) {
return true;
}
break;
case WILD_PCP_L3SADR_TOS_L4SPRT:
if ((srcSwitchDpid == o.srcSwitchDpid) &&
(destL4Port == o.destL4Port) &&
(inputPort == o.inputPort) &&
(etherType == o.etherType) &&
(destIpAddr == o.destIpAddr) &&
(ofPri == o.ofPri) &&
(protocol == o.protocol)) {
return true;
}
break;
case WILD_PCP_L3DADR_TOS_L4DPRT:
if ((srcSwitchDpid == o.srcSwitchDpid) &&
(srcL4Port == o.srcL4Port) &&
(inputPort == o.inputPort) &&
(etherType == o.etherType) &&
(srcIpAddr == o.srcIpAddr) &&
(ofPri == o.ofPri) &&
(protocol == o.protocol)) {
return true;
}
break;
case WILD_PCP_L3PROT_TOS:
if ((srcSwitchDpid == o.srcSwitchDpid) &&
(srcL4Port == o.srcL4Port) &&
(destL4Port == o.destL4Port) &&
(inputPort == o.inputPort) &&
(etherType == o.etherType) &&
(srcIpAddr == o.srcIpAddr) &&
(destIpAddr == o.destIpAddr) &&
(ofPri == o.ofPri)) {
return true;
}
break;
case WILD_NONE: /* None of the match fields are wild-carded */
if ((srcSwitchDpid == o.srcSwitchDpid) &&
(srcL4Port == o.srcL4Port) &&
(destL4Port == o.destL4Port) &&
(inputPort == o.inputPort) &&
(etherType == o.etherType) &&
(srcIpAddr == o.srcIpAddr) &&
(destIpAddr == o.destIpAddr) &&
(ofPri == o.ofPri) &&
(protocol == o.protocol) &&
(nwTos == o.nwTos) &&
(pcp == o.pcp)) {
return true;
}
break;
default:
if (logger.isDebugEnabled()) {
logger.debug("Unexpected wildcard {} in {}", wildcards, o);
}
if ((srcSwitchDpid == o.srcSwitchDpid) &&
(srcL4Port == o.srcL4Port) &&
(destL4Port == o.destL4Port) &&
(inputPort == o.inputPort) &&
(etherType == o.etherType) &&
(srcIpAddr == o.srcIpAddr) &&
(destIpAddr == o.destIpAddr) &&
(ofPri == o.ofPri) &&
(protocol == o.protocol) &&
(nwTos == o.nwTos) &&
(pcp == o.pcp)) {
return true;
}
break;
}
return false;
}
/* This comparison function works only for OF v1.0. */
private boolean compareWithFce(OFMatchWithSwDpid ofmWithSwDpid, Short pri) {
/* First check the wildcards since that determines how rest of the
* OFMatch fields are relevant for equals comparison
*
* Later versions of OVS switch (e.g. the one bundled with
* big-switch controller doesn't return the exact same wildcard that
* was sent when flow-mod was programmed.
* Offical openflow vm does return the same wildcard though
* Openflow spec. 1.0 requires that the wildcard, match and priority
* fields be preserved across flow-mod program and removal messages
* In order to work with OVS, making some adjustments to the
* wildcard in ofm received in flow-removal message. Correctness is
* maintained.
*/
int ofmWildcards = ofmWithSwDpid.getOfMatch().getWildcards();
ofmWildcards |= 0x100000; /* Since VLAN is wildcarded */
if ((ofmWildcards & OFMatch.OFPFW_NW_SRC_ALL) != 0) {
/* Here 0x100000 is same as 0x1XXXXX */
ofmWildcards |= OFMatch.OFPFW_NW_SRC_MASK;
}
if ((ofmWildcards & OFMatch.OFPFW_NW_DST_ALL) != 0) {
/* Here 0x100000 is same as 0x1XXXXX */
ofmWildcards |= OFMatch.OFPFW_NW_DST_MASK;
}
if (srcSwitchDpid != ofmWithSwDpid.getSwitchDataPathId()) {
return false;
}
if (wildcards != ofmWildcards) {
return false;
}
switch (wildcards) {
case WILD_MATCH_INP_VLAN_DLADRS: /* Edge ports */
if ((srcSwitchDpid == ofmWithSwDpid.getSwitchDataPathId()) &&
(inputPort == ofmWithSwDpid.getOfMatch().getInputPort()) &&
(ofPri == pri)) {
return true;
}
break;
case WILD_MATCH_INP_VLAN_DLADRS_ET: /* permit ip any any */
if ((srcSwitchDpid == ofmWithSwDpid.getSwitchDataPathId()) &&
(inputPort == ofmWithSwDpid.getOfMatch().getInputPort()) &&
(etherType == ofmWithSwDpid.getOfMatch().getDataLayerType()) &&
(ofPri == pri)) {
return true;
}
break;
case WILD_MATCH_INP_VLAN_DLADRS_ET_PROTO_DP:
if ((srcSwitchDpid == ofmWithSwDpid.getSwitchDataPathId()) &&
(destL4Port == ofmWithSwDpid.getOfMatch().getTransportDestination()) &&
(inputPort == ofmWithSwDpid.getOfMatch().getInputPort()) &&
(etherType == ofmWithSwDpid.getOfMatch().getDataLayerType()) &&
(ofPri == pri) &&
(protocol == ofmWithSwDpid.getOfMatch().getNetworkProtocol())) {
return true;
}
break;
case WILD_MATCH_INP_VLAN_DLADRS_ET_PROTO:
if ((srcSwitchDpid == ofmWithSwDpid.getSwitchDataPathId()) &&
(inputPort == ofmWithSwDpid.getOfMatch().getInputPort()) &&
(etherType == ofmWithSwDpid.getOfMatch().getDataLayerType()) &&
(ofPri == pri) &&
(protocol == ofmWithSwDpid.getOfMatch().getNetworkProtocol())) {
return true;
}
break;
case WILD_MATCH_VLAN_DLSADR: // Core or internal ports
if (srcSwitchDpid == ofmWithSwDpid.getSwitchDataPathId()) {
return true;
}
break;
case WILD_MATCH_INP_VLAN_DLADRS_ETYP_NWADRS:
if ((srcSwitchDpid == ofmWithSwDpid.getSwitchDataPathId()) &&
(inputPort == ofmWithSwDpid.getOfMatch().getInputPort()) &&
(etherType == ofmWithSwDpid.getOfMatch().getDataLayerType()) &&
(srcIpAddr == ofmWithSwDpid.getOfMatch().getNetworkSource()) &&
(destIpAddr == ofmWithSwDpid.getOfMatch().getNetworkDestination()) &&
(ofPri == pri)) {
return true;
}
break;
case WILD_PCP_TOS_L4PSPRT:
if ((srcSwitchDpid == ofmWithSwDpid.getSwitchDataPathId()) &&
(destL4Port == ofmWithSwDpid.getOfMatch().getTransportDestination()) &&
(inputPort == ofmWithSwDpid.getOfMatch().getInputPort()) &&
(etherType == ofmWithSwDpid.getOfMatch().getDataLayerType()) &&
(srcIpAddr == ofmWithSwDpid.getOfMatch().getNetworkSource()) &&
(destIpAddr == ofmWithSwDpid.getOfMatch().getNetworkDestination()) &&
(ofPri == pri) &&
(protocol == ofmWithSwDpid.getOfMatch().getNetworkProtocol())) {
return true;
}
break;
case WILD_PCP_TOS_L4PDPRT:
if ((srcSwitchDpid == ofmWithSwDpid.getSwitchDataPathId()) &&
(srcL4Port == ofmWithSwDpid.getOfMatch().getTransportSource()) &&
(inputPort == ofmWithSwDpid.getOfMatch().getInputPort()) &&
(etherType == ofmWithSwDpid.getOfMatch().getDataLayerType()) &&
(srcIpAddr == ofmWithSwDpid.getOfMatch().getNetworkSource()) &&
(destIpAddr == ofmWithSwDpid.getOfMatch().getNetworkDestination()) &&
(ofPri == pri) &&
(protocol == ofmWithSwDpid.getOfMatch().getNetworkProtocol())) {
return true;
}
break;
case WILD_PCP_L3SADR_TOS_L4SPRT:
if ((srcSwitchDpid == ofmWithSwDpid.getSwitchDataPathId()) &&
(destL4Port == ofmWithSwDpid.getOfMatch().getTransportDestination()) &&
(inputPort == ofmWithSwDpid.getOfMatch().getInputPort()) &&
(etherType == ofmWithSwDpid.getOfMatch().getDataLayerType()) &&
(destIpAddr == ofmWithSwDpid.getOfMatch().getNetworkDestination()) &&
(ofPri == pri) &&
(protocol == ofmWithSwDpid.getOfMatch().getNetworkProtocol())) {
return true;
}
break;
case WILD_PCP_L3DADR_TOS_L4DPRT:
if ((srcSwitchDpid == ofmWithSwDpid.getSwitchDataPathId()) &&
(srcL4Port == ofmWithSwDpid.getOfMatch().getTransportSource()) &&
(inputPort == ofmWithSwDpid.getOfMatch().getInputPort()) &&
(etherType == ofmWithSwDpid.getOfMatch().getDataLayerType()) &&
(ofPri == pri) &&
(protocol == ofmWithSwDpid.getOfMatch().getNetworkProtocol())) {
return true;
}
break;
case WILD_PCP_L3PROT_TOS:
if ((srcSwitchDpid == ofmWithSwDpid.getSwitchDataPathId()) &&
(srcL4Port == ofmWithSwDpid.getOfMatch().getTransportSource()) &&
(destL4Port == ofmWithSwDpid.getOfMatch().getTransportDestination()) &&
(inputPort == ofmWithSwDpid.getOfMatch().getInputPort()) &&
(etherType == ofmWithSwDpid.getOfMatch().getDataLayerType()) &&
(srcIpAddr == ofmWithSwDpid.getOfMatch().getNetworkSource()) &&
(destIpAddr == ofmWithSwDpid.getOfMatch().getNetworkDestination()) &&
(ofPri == pri)){
return true;
}
break;
default:
if (logger.isTraceEnabled()) {
logger.trace("Unexpected wildcard {} in Match {}",
wildcards, ofmWithSwDpid);
}
if (srcSwitchDpid != ofmWithSwDpid.getSwitchDataPathId()) {
return false;
}
if (((wildcards & OFMatch.OFPFW_IN_PORT) == 0) &&
(inputPort != ofmWithSwDpid.getOfMatch().getInputPort())) {
return false;
}
if (((wildcards & OFMatch.OFPFW_TP_DST) == 0) &&
(destL4Port != ofmWithSwDpid.getOfMatch().getTransportDestination())) {
return false;
}
if (((wildcards & OFMatch.OFPFW_TP_SRC) == 0) &&
(srcL4Port != ofmWithSwDpid.getOfMatch().getTransportSource())) {
return false;
}
if (((wildcards & OFMatch.OFPFW_DL_TYPE) == 0) &&
(etherType != ofmWithSwDpid.getOfMatch().getDataLayerType())) {
return false;
}
if (((wildcards & OFMatch.OFPFW_NW_PROTO) == 0) &&
(protocol != ofmWithSwDpid.getOfMatch().getNetworkProtocol())) {
return false;
}
if (((wildcards & OFMatch.OFPFW_NW_TOS) == 0) &&
(nwTos != ofmWithSwDpid.getOfMatch().getNetworkTypeOfService())) {
return false;
}
if (ofPri != pri) {
return false;
}
int nwAddrPrefixLen = Math.max(32 -
((wildcards & OFMatch.OFPFW_NW_SRC_MASK) >>
OFMatch.OFPFW_NW_SRC_SHIFT), 0);
int mask = ~((1 << (32 - nwAddrPrefixLen)) - 1);
if ((srcIpAddr & mask) != (ofmWithSwDpid.getOfMatch().getNetworkSource() & mask)) {
return false;
}
nwAddrPrefixLen = Math.max(32 -
((wildcards & OFMatch.OFPFW_NW_DST_MASK) >>
OFMatch.OFPFW_NW_DST_SHIFT), 0);
mask = ~((1 << (32 - nwAddrPrefixLen)) - 1);
if ((destIpAddr & mask) !=
(ofmWithSwDpid.getOfMatch().getNetworkDestination() & mask)) {
return false;
}
return true;
}
return false;
}
//****************************************
// Getters for REST API, Custom Serializer
//****************************************
/**
* Get the source switch's data-path id in the flow cache entry object
* @return data path id of the source switch of the flow in its cluster
*/
public long getSrcSwitchDpid() {
return srcSwitchDpid;
}
/**
* Get the source transport port in the flow cache entry object
* @return source transport port
*/
public short getSrcL4Port() {
return srcL4Port;
}
/**
* Get the destination transport port in the flow cache entry object
* @return destination transport port
*/
public short getDestL4Port() {
return destL4Port;
}
/**
* Get the source switch's input port in the flow cache entry object
* @return input port of the source switch of the flow
*/
public short getInputPort() {
return inputPort;
}
/**
* Get the Ether type in the flow cache entry object
* @return ether type of the flow
*/
public short getEtherType() {
return etherType;
}
/**
* Get the source network address of the flow cache entry object
* @return source network address of the flow
*/
public int getSrcIpAddr() {
return srcIpAddr;
}
/**
* Get the destination network address of the flow cache entry object
* @return destination network address of the flow
*/
public int getDestIpAddr() {
return destIpAddr;
}
/**
* Get the wildcard field in the flow cache entry object
* @return the wildcard field of the flow
*/
public int getWildcards() {
return wildcards;
}
/**
* Get the flow-mod priority the flow cache entry object
* @return flow-mod priority
*/
public short getOfPri() {
return ofPri;
}
/**
* Get the network protocol field in the flow entry object
* @return network protocol field
*/
public byte getProtocol() {
return protocol;
}
/**
* Get the state of the flow such as FCStateACTIVE, FCStateINACTIVE,
* FCStateUNUSED.
* @return state of the flow
*/
public byte getState() {
return state;
}
/**
* Get the PERMIT or DENY action associated with the flow entry
* @return permit or deny action of the flow entry
*/
public byte getAction() {
return action;
}
/**
* Get the network type of service field in the flow entry
* @return network type of service field
*/
public byte getNwTos() {
return nwTos;
}
/**
* Get the 802.1Q priority code point in the flow entry
* @return priority code point
*/
public byte getPcp() {
return pcp;
}
public byte getScanCount() {
return scanCnt;
}
@Override
public String toString() {
String str = "SwDpid: " + HexString.toHexString(srcSwitchDpid) + " ";
str += "srcL4Port: " + srcL4Port + " ";
str += "destL4Port: " + destL4Port + " ";
str += "inputPort: " + inputPort + " ";
str += "etherType: " + etherType + " ";
str += "srcIpAddr: " + IPv4.fromIPv4Address(srcIpAddr) + " ";
str += "destIpAddr: " + IPv4.fromIPv4Address(destIpAddr) + " ";
str += "wildcards: 0x" + Integer.toHexString(wildcards);
str += "ofPri: " + ofPri + " ";
str += "protocol: " + protocol + " ";
str += "state: " + state + " ";
str += "action: " + action + " ";
str += "nwTos: " + nwTos + " ";
str += "pcp: " + pcp + " ";
str += "scanCnt: " + scanCnt;
return str;
}
}
private FCEntry getFCEntryByOFMatchAndPriority(OFMatchWithSwDpid ofmWithSwDpid, short pri) {
if (fce.compareWithFce(ofmWithSwDpid, pri)) {
return fce;
}
if (fceList != null) {
for (int idx=0; idx < fceList.size(); idx++) {
if (fceList.get(idx).compareWithFce(ofmWithSwDpid, pri)) {
return fceList.get(idx);
}
}
}
// Not found
return null;
}
/**
* Returns FCEntry if it is in the FlowCacheObj otherwise returns null
* @param ofmWithSwDpid
* @param priority
* @param action
* @return flow cache entry object
*/
private FCEntry getFCEntry(OFMatchWithSwDpid ofmWithSwDpid, short priority, byte action) {
if (fce.compareWithFce(ofmWithSwDpid, priority) && (fce.action == action)) {
return fce;
}
if (fceList != null) {
FCEntry tempFce;
int fceListSize = fceList.size();
for (int idx=0; idx < fceListSize; idx++) {
tempFce = fceList.get(idx);
if (tempFce.compareWithFce(ofmWithSwDpid, priority) &&
(tempFce.action == action)) {
return tempFce;
}
}
}
// Not found
return null;
}
protected FCEntry getFCEntryWithCookie(
OFMatchWithSwDpid ofmWithSwDpid, long cookie, short priority, byte action) {
if (this.cookie != cookie) {
return null;
}
return getFCEntry(ofmWithSwDpid, priority, action);
}
private FCEntry findFreeFce() {
// Only fce entry can be in unused state. Entries in fceList are in
// either active or inactive state
if (fce.state != FCStateACTIVE) {
return fce;
}
if (fceList != null) {
int fceListSize = fceList.size();
for (int idx=0; idx < fceListSize; idx++) {
if (fceList.get(idx).state == FCStateINACTIVE) {
return fceList.get(idx);
}
}
}
// No free entry found
return null;
}
private void initFCObj(OFMatchWithSwDpid ofmWithSwDpid, long cookie,
short priority, byte action) {
fceList = null;
fce.fcEntryInit(ofmWithSwDpid, priority, action);
fce.state = FCStateACTIVE;
this.cookie = cookie;
}
/**
* Stores a flow in the flow object
* @param ofmWithSwDpid the flow match structure
* @param cookie the flow-mod cookie
* @param priority the flow mod priority
* @param action the flow mod action - PERMIT or DENY
* @param bfc the flow cache object into which the flow if to be inserted
* @return
*/
protected FCOper storeFCEntry(OFMatchWithSwDpid ofmWithSwDpid, Long cookie,
short priority, byte action, BetterFlowCache bfc) {
/* Get the time. We seem to need it in all cases! */
long curTimeNs = System.nanoTime();
/* If Cookie is different then reset the FlowCacheObj and
* add this entry
*/
if (this.cookie != cookie) {
/* TODO - check match */
initFCObj(ofmWithSwDpid, cookie, priority, action);
installTimeNs = curTimeNs;
return FCOper.NEW_ENTRY;
}
/* Cookie is same - check if the FC entry is already there */
FCEntry tempFce = getFCEntry(ofmWithSwDpid, priority, action);
if (tempFce != null) {
/* Entry already there, see if this add need to be dampened */
if (tempFce.state == FCStateINACTIVE) {
/* Flow cache is in removed state. This means that the flow
* cache entry was not deleted to avoid frequent creation and
* deletion of objects. Make it active
*/
tempFce.state = FCStateACTIVE;
installTimeNs = curTimeNs;
return FCOper.ACTIVATED;
}
/* Flow mod is in flow cache and it is in active state */
if ((curTimeNs - installTimeNs) < DAMPEN_TIMEOUT_NS) {
/* flow mod was installed less than DAMPEN_TIMEOUT_NS.
* Dampen this flow mod, so that it is not installed again now
*/
return FCOper.DAMPENED;
} else {
installTimeNs = curTimeNs;
return FCOper.NOT_DAMPENED;
}
}
/* New flow mod, store it
* Check if the OF Match exists (action is different)
*/
installTimeNs = curTimeNs;
tempFce = getFCEntryByOFMatchAndPriority(ofmWithSwDpid, priority);
if (tempFce != null) {
/* Found FC Entry with same OFMatch, update this entry */
if (tempFce.state == FCStateINACTIVE) {
tempFce.state = FCStateACTIVE;
tempFce.action = action;
return FCOper.ACTIVATED;
} else {
// Entry already in active state
tempFce.action = action;
return FCOper.NOP;
}
}
/* OF Match doesn't exist add it to flow cache fce is never set to
* null, instead the whole FlowCacheObj would be deleted
*/
tempFce = findFreeFce(); /* Returns an entry in inactive state */
if (tempFce != null) {
byte state = tempFce.state;
tempFce.fcEntryInit(ofmWithSwDpid, priority, action);
if (state == FCStateINACTIVE) {
return FCOper.ACTIVATED;
} else {
return FCOper.NEW_ENTRY;
}
} else {
/* Check if the flow cache is full */
if (bfc.getBfcCore().isFlowCacheFull()) {
/* Update the cookie indicating that the flow is not stored in
* the cache
* cookie |= FC_COOKIE_NOT_FLOW_STORED_IN_CACHE;
*/
return FCOper.NOT_STORED_FULL;
} else { /* Flow cache is not full */
/* fce was not free, no free entry found in array or array
* was null create a new FCEntry and store it in array
*/
tempFce = new FCEntry(ofmWithSwDpid, priority, action);
if (fceList == null) {
fceList = new ArrayList<FCEntry>();
}
fceList.add(tempFce);
return FCOper.NEW_ENTRY;
}
}
}
/**
* Remove FCEntry - this method doesn't actually remove the entry from the
* flow cache. It marks the entry, if found, inactive. If the same flow is
* installed soon enough then a bunch of objects can be reused instead of
* being recreated. All inactive entries are removed every time flow-cache
* scan-interval period. (Flow cache is scanned periodically to take care
* of entries for which flow-mod removal messages were lost.
* @param ofmWithSwDpid
* @param priority
* @return
*/
protected FCOper removeFCEntry(OFMatchWithSwDpid ofmWithSwDpid, short priority) {
/* Get the entry */
FCEntry tempFce = getFCEntryByOFMatchAndPriority(ofmWithSwDpid, priority);
if (tempFce != null) {
if (tempFce.state != FCStateINACTIVE) {
if (logger.isTraceEnabled()) {
logger.trace("removeFCEntry: inactivate flow fce {}", tempFce);
}
tempFce.state = FCStateINACTIVE;
tempFce.scanCnt = 0;
return FCOper.DEACTIVATED;
} else { // entry already in inactive state
return FCOper.NOT_ACTIVE;
}
}
return FCOper.NOT_FOUND;
}
/**
* Delete FCEntry - this method actually removes the entry from the
* flow cache.
* @param ofmWithSwDpid
* @param priority
* @return
*/
protected FCOper deleteFCEntry(OFMatchWithSwDpid ofmWithSwDpid, short priority,
BetterFlowCache bfc) {
boolean found = false;
/* Check if fce is this entry */
if (fce.compareWithFce(ofmWithSwDpid, priority)) {
/* found the entry */
if ((fceList == null) || fceList.isEmpty()) {
/* adjust count since the FlowCacheObj would be deleted */
if (fce.state == FCStateACTIVE) {
bfc.getBfcCore().updateCountsLocal(FCOper.DELETED_ACTIVE);
} else {
bfc.getBfcCore().updateCountsLocal(FCOper.DELETED_INACTIVE);
}
return FCOper.FCOBJ_FREE;
} else {
if (fce.state == FCStateACTIVE) {
/* FlowCacheObj can't be freed */
if (logger.isTraceEnabled()) {
logger.trace("deleteFCEntry: inactivate flow fce {}",
fce);
}
fce.state = FCStateINACTIVE;
fce.scanCnt = 0;
return FCOper.DEACTIVATED;
} else {
return FCOper.NOT_ACTIVE;
}
}
} else {
/* Not found in fce, check fceList */
if (fceList != null) {
/* Check if it is in the array
* User iterator since we may have to delete the entry */
ListIterator< FCEntry> iter = fceList.listIterator();
while (iter.hasNext()) {
FCEntry e = iter.next();
if (e.compareWithFce(ofmWithSwDpid, priority)) {
// Found the entry
found = true;
if (e.state == FCStateACTIVE) {
bfc.getBfcCore()
.updateCountsLocal(FCOper.DELETED_ACTIVE);
} else {
bfc.getBfcCore()
.updateCountsLocal(FCOper.DELETED_INACTIVE);
}
iter.remove();
break;
}
}
}
if (found) {
if ((fce.state != FCStateACTIVE) && (fceList.isEmpty())) {
fceList = null;
return FCOper.FCOBJ_FREE;
}
return FCOper.NOP;
} else {
return FCOper.NOT_FOUND;
}
}
}
/**
* Delete FCEntry if the source switch of the entry matches a given switch
* - this method actually removes the entry from the flow cache.
* @param switch id
* @return
*/
protected FCOper deleteFCEntry(long swId, BetterFlowCache bfc) {
/* Check if fce is on the given switch id. */
int numRemovedActiveFlows = 0;
int numRemovedInactiveFlows = 0;
int numDelFlows = 0;
if (fce.srcSwitchDpid == swId) {
/* found the entry */
if (fce.state == FCStateACTIVE) {
if (logger.isTraceEnabled()) {
logger.trace("deleteFCEntry 2: inactivate flow fce {}", fce);
}
fce.state = FCStateINACTIVE;
numRemovedActiveFlows++;
fce.scanCnt = 0;
} else if (fce.state == FCStateINACTIVE) {
numRemovedInactiveFlows++;
}
numDelFlows++;
}
/* Check fceList */
if (fceList != null) {
/* Check if it is in the array
* User iterator since we may have to delete the entry */
ListIterator< FCEntry> iter = fceList.listIterator();
while (iter.hasNext()) {
FCEntry e = iter.next();
if (e.srcSwitchDpid == swId) {
// match found
switch (e.state) {
case FCStateACTIVE:
numRemovedActiveFlows++;
break;
case FCStateINACTIVE:
numRemovedInactiveFlows++;
break;
default:
break;
}
numDelFlows++;
iter.remove();
}
}
}
bfc.getBfcCore().fcLocalCounters.get().activeCnt -= numRemovedActiveFlows;
bfc.getBfcCore().fcLocalCounters.get().inactiveCnt -= numRemovedInactiveFlows;
bfc.getBfcCore().fcLocalCounters.get().delCnt += numDelFlows;
if (fce.srcSwitchDpid == swId &&
((fceList == null) || fceList.isEmpty())) {
return FCOper.FCOBJ_FREE;
}
return FCOper.NOP;
}
/**
* Deactivate stale FCEntry - entries with scanCount greater than 2.
*
* @return is the flow cache object can be deleted from map or not
*/
protected FCOper deactivateStaleFlows(int staleScanCnt, BetterFlowCache bfc) {
int numDeletedFlows = 0;
/* Check if fce is stale. Consider only active flows */
if (fce.state == FCStateACTIVE) {
if (fce.scanCnt > staleScanCnt) {
/* found the entry */
if (logger.isTraceEnabled()) {
logger.trace("deleteStaleFlows: inactivate flow fce {}", fce);
}
fce.state = FCStateINACTIVE;
fce.scanCnt = 0;
numDeletedFlows++;
} else {
fce.scanCnt++;
}
}
/* Check fceList */
if (fceList != null) {
/* Check if it is in the array
* User iterator since we may have to delete the entry */
ListIterator< FCEntry> iter = fceList.listIterator();
while (iter.hasNext()) {
FCEntry e = iter.next();
if (e.state == FCStateACTIVE) {
if (e.scanCnt > staleScanCnt) {
e.state = FCStateINACTIVE;
e.scanCnt = 0;
numDeletedFlows++;
} else {
e.scanCnt++;
}
}
}
}
if (numDeletedFlows > 0) {
bfc.getBfcCore().fcLocalCounters.get().activeCnt -= numDeletedFlows;
bfc.getBfcCore().fcLocalCounters.get().inactiveCnt += numDeletedFlows;
}
return FCOper.NOP;
}
/**
* Removes all inactive entries from fceList
* @return true if neither of fce and fceList contain any ACTIVE flow
* false otherwise
*/
protected boolean deleteInactiveFlows() {
if (fceList != null) {
ListIterator< FCEntry> iter = fceList.listIterator();
while (iter.hasNext()) {
if (iter.next().state != FCStateACTIVE) {
iter.remove();
}
}
}
if (((fceList == null) || fceList.isEmpty()) &&
(fce.state != FCStateACTIVE)) {
return true; /* this FlowCacheObj can be freed */
}
return false;
}
/**
* Removes all inactive entries from fceList
* @return true if neither of fce and fceList contain any ACTIVE flow
* false otherwise
*/
protected boolean deleteFlowsBySwitch(long switchDpid, BetterFlowCache bfc) {
int numActiveFlows = 0;
int numInactiveFlows = 0;
if ((fce.state == FCStateACTIVE) &&
(fce.srcSwitchDpid == switchDpid)) {
if (logger.isTraceEnabled()) {
logger.trace("deleteFlowsBySwitch: inactivate flow fce {}", fce);
}
fce.state = FCStateINACTIVE;
fce.scanCnt = 0;
numActiveFlows++;
numInactiveFlows++;
}
if (fceList != null) {
ListIterator< FCEntry> iter = fceList.listIterator();
while (iter.hasNext()) {
FCEntry e = iter.next();
if (e.srcSwitchDpid == switchDpid) {
if (e.state == FCStateACTIVE) {
numActiveFlows++;
} else {
numInactiveFlows--;
}
iter.remove();
}
}
}
if (((fceList == null) || fceList.isEmpty()) &&
(fce.state != FCStateACTIVE)) {
if (numActiveFlows > 0) {
bfc.getBfcCore().fcLocalCounters.get().activeCnt -= numActiveFlows;
}
return true; /* this FlowCacheObj can be freed */
} else {
bfc.getBfcCore().fcLocalCounters.get().activeCnt -= numActiveFlows;
bfc.getBfcCore().fcLocalCounters.get().inactiveCnt += numInactiveFlows;
return false;
}
}
}