From 5e994da833b25b180b8ed4aecd39b19783d41644 Mon Sep 17 00:00:00 2001
From: Ryan Izard <rizard@g.clemson.edu>
Date: Thu, 5 Feb 2015 17:41:01 -0500
Subject: [PATCH] SFP supports hard/idle timouts w/drawback of needlessly
 sending flow_del to switch (which gets silently discarded). This is a good
 first step but needs to be followed up with a solution to the redundant
 flow_del msg. Also, patched MatchSerializer to display masks if present and
 fixed a bug in OFInstructionListSerializer for metadata and goto_table
 instructions. Added masking support to all SFP match fields. OVS supports
 some masked fields that are not defined in the OF1.3 spec. Oddly enough, OVS
 returns a bad-match error message even though the flow gets inserted. I
 suppose that message is due to the OF protocol not supporting it. OVS should
 squelch it if it supports it though. In any case, Floodlight is equipped to
 support all present MatchFields as masked MatchFields in the future. To
 specify a mask to any field (beside MPLS BOS, which is a boolean), use a
 CIDR-style '/' after the value and before the mask. All values and masks
 support decimal or hexadecimal (with leading '0x'), with the exception of all
 values that are MacAddress or IPAddress types -- they only support hex-string
 or dotted-decimal, respectively. Lastly, an English typo has been fixed in
 IStaticFlowEntryPusherService.

---
 logback.xml                                   |   2 +-
 .../core/web/serializers/MatchSerializer.java |  82 +++---
 .../OFInstructionListSerializer.java          |   8 +-
 .../IStaticFlowEntryPusherService.java        |   2 +-
 .../staticflowentry/StaticFlowEntries.java    |  11 +-
 .../StaticFlowEntryPusher.java                | 107 +++++--
 .../util/InstructionUtils.java                |   2 +-
 .../floodlightcontroller/util/MatchUtils.java | 267 ++++++++++++------
 8 files changed, 329 insertions(+), 152 deletions(-)

diff --git a/logback.xml b/logback.xml
index a25cc7f63..f5163007d 100644
--- a/logback.xml
+++ b/logback.xml
@@ -11,5 +11,5 @@
   <logger name="LogService" level="DEBUG"/> <!-- Restlet access logging -->
   <logger name="net.floodlightcontroller" level="DEBUG"/>
   <logger name="net.floodlightcontroller.logging" level="DEBUG"/>
-  <logger name="org.sdnplatform" level="DEBUG"/>
+  <logger name="org.sdnplatform" level="INFO"/>
 </configuration>
diff --git a/src/main/java/net/floodlightcontroller/core/web/serializers/MatchSerializer.java b/src/main/java/net/floodlightcontroller/core/web/serializers/MatchSerializer.java
index c242cc630..9bc587ced 100644
--- a/src/main/java/net/floodlightcontroller/core/web/serializers/MatchSerializer.java
+++ b/src/main/java/net/floodlightcontroller/core/web/serializers/MatchSerializer.java
@@ -31,6 +31,11 @@ public class MatchSerializer extends JsonSerializer<Match> {
 	JsonProcessingException {
 		serializeMatch(jGen, match);
 	}
+	
+	@SuppressWarnings("unchecked") 
+	public static String matchValueToString(Match m, @SuppressWarnings("rawtypes") MatchField mf) {
+		return m.isPartiallyMasked(mf) ? m.getMasked(mf).toString() : m.get(mf).toString();
+	}
 
 	public static void serializeMatch(JsonGenerator jGen, Match match) throws IOException, JsonProcessingException {
 		// list flow matches
@@ -42,115 +47,116 @@ public class MatchSerializer extends JsonSerializer<Match> {
 			MatchField<?> mf = mi.next();
 			switch (mf.id) {
 			case IN_PORT:
-				jGen.writeStringField(MatchUtils.STR_IN_PORT, m.get(MatchField.IN_PORT).toString());
+				jGen.writeStringField(MatchUtils.STR_IN_PORT, matchValueToString(m, mf));
 				break;
 			case IN_PHY_PORT:
-				jGen.writeStringField(MatchUtils.STR_IN_PHYS_PORT, m.get(MatchField.IN_PHY_PORT).toString());
+				jGen.writeStringField(MatchUtils.STR_IN_PHYS_PORT, matchValueToString(m, mf));
 				break;
 			case ARP_OP:
-				jGen.writeNumberField(MatchUtils.STR_ARP_OPCODE, m.get(MatchField.ARP_OP).getOpcode());
+				jGen.writeStringField(MatchUtils.STR_ARP_OPCODE, matchValueToString(m, mf));
 				break;
 			case ARP_SHA:
-				jGen.writeStringField(MatchUtils.STR_ARP_SHA, m.get(MatchField.ARP_SHA).toString());
+				jGen.writeStringField(MatchUtils.STR_ARP_SHA, matchValueToString(m, mf));
 				break;
 			case ARP_SPA:
-				jGen.writeStringField(MatchUtils.STR_ARP_SPA, m.get(MatchField.ARP_SPA).toString());
+				jGen.writeStringField(MatchUtils.STR_ARP_SPA, matchValueToString(m, mf));
 				break;
 			case ARP_THA:
-				jGen.writeStringField(MatchUtils.STR_ARP_DHA, m.get(MatchField.ARP_THA).toString());
+				jGen.writeStringField(MatchUtils.STR_ARP_DHA, matchValueToString(m, mf));
 				break;
 			case ARP_TPA:
-				jGen.writeStringField(MatchUtils.STR_ARP_DPA, m.get(MatchField.ARP_TPA).toString());
+				jGen.writeStringField(MatchUtils.STR_ARP_DPA, matchValueToString(m, mf));
 				break;
-			case ETH_TYPE:
-				jGen.writeNumberField(MatchUtils.STR_DL_TYPE, m.get(MatchField.ETH_TYPE).getValue());
+			case ETH_TYPE: // TODO Remove this "0x" when Loxigen is updated.
+				jGen.writeStringField(MatchUtils.STR_DL_TYPE, m.isPartiallyMasked(mf) ?
+						"0x" + m.getMasked(mf).toString() : "0x" + m.get(mf).toString());
 				break;
 			case ETH_SRC:
-				jGen.writeStringField(MatchUtils.STR_DL_SRC, m.get(MatchField.ETH_SRC).toString());
+				jGen.writeStringField(MatchUtils.STR_DL_SRC, matchValueToString(m, mf));
 				break;
 			case ETH_DST:
-				jGen.writeStringField(MatchUtils.STR_DL_DST, m.get(MatchField.ETH_DST).toString());
+				jGen.writeStringField(MatchUtils.STR_DL_DST, matchValueToString(m, mf));
 				break;
 			case VLAN_VID:
-				jGen.writeNumberField(MatchUtils.STR_DL_VLAN, m.get(MatchField.VLAN_VID).getVlan());
+				jGen.writeStringField(MatchUtils.STR_DL_VLAN, matchValueToString(m, mf));
 				break;
 			case VLAN_PCP:
-				jGen.writeNumberField(MatchUtils.STR_DL_VLAN_PCP, m.get(MatchField.VLAN_PCP).getValue());
+				jGen.writeStringField(MatchUtils.STR_DL_VLAN_PCP, matchValueToString(m, mf));
 				break;
 			case ICMPV4_TYPE:
-				jGen.writeNumberField(MatchUtils.STR_ICMP_TYPE, m.get(MatchField.ICMPV4_TYPE).getType());
+				jGen.writeStringField(MatchUtils.STR_ICMP_TYPE, matchValueToString(m, mf));
 				break;
 			case ICMPV4_CODE:
-				jGen.writeNumberField(MatchUtils.STR_ICMP_CODE, m.get(MatchField.ICMPV4_CODE).getCode());
+				jGen.writeStringField(MatchUtils.STR_ICMP_CODE, matchValueToString(m, mf));
 				break;
 			case ICMPV6_TYPE:
-				jGen.writeNumberField(MatchUtils.STR_ICMPV6_TYPE, m.get(MatchField.ICMPV6_TYPE).getValue());
+				jGen.writeStringField(MatchUtils.STR_ICMPV6_TYPE, matchValueToString(m, mf));
 				break;
 			case ICMPV6_CODE:
-				jGen.writeNumberField(MatchUtils.STR_ICMPV6_CODE, m.get(MatchField.ICMPV6_CODE).getValue());
+				jGen.writeStringField(MatchUtils.STR_ICMPV6_CODE, matchValueToString(m, mf));
 				break;
 			case IP_DSCP:
-				jGen.writeNumberField(MatchUtils.STR_NW_DSCP, m.get(MatchField.IP_DSCP).getDscpValue());
+				jGen.writeStringField(MatchUtils.STR_NW_DSCP, matchValueToString(m, mf));
 				break;
 			case IP_ECN:
-				jGen.writeNumberField(MatchUtils.STR_NW_ECN, m.get(MatchField.IP_ECN).getEcnValue());
+				jGen.writeStringField(MatchUtils.STR_NW_ECN, matchValueToString(m, mf));
 				break;
 			case IP_PROTO:
-				jGen.writeNumberField(MatchUtils.STR_NW_PROTO, m.get(MatchField.IP_PROTO).getIpProtocolNumber());
+				jGen.writeStringField(MatchUtils.STR_NW_PROTO, matchValueToString(m, mf));
 				break;
 			case IPV4_SRC:
-				jGen.writeStringField(MatchUtils.STR_NW_SRC, m.get(MatchField.IPV4_SRC).toString());
+				jGen.writeStringField(MatchUtils.STR_NW_SRC, matchValueToString(m, mf));
 				break;
 			case IPV4_DST:
-				jGen.writeStringField(MatchUtils.STR_NW_DST, m.get(MatchField.IPV4_DST).toString());
+				jGen.writeStringField(MatchUtils.STR_NW_DST, matchValueToString(m, mf));
 				break;
 			case IPV6_SRC:
-				jGen.writeStringField(MatchUtils.STR_IPV6_SRC, m.get(MatchField.IPV6_SRC).toString());
+				jGen.writeStringField(MatchUtils.STR_IPV6_SRC, matchValueToString(m, mf));
 				break;
 			case IPV6_DST:
-				jGen.writeStringField(MatchUtils.STR_IPV6_DST, m.get(MatchField.IPV6_DST).toString());
+				jGen.writeStringField(MatchUtils.STR_IPV6_DST, matchValueToString(m, mf));
 				break;
 			case IPV6_FLABEL:
-				jGen.writeNumberField(MatchUtils.STR_IPV6_FLOW_LABEL, m.get(MatchField.IPV6_FLABEL).getIPv6FlowLabelValue());
+				jGen.writeStringField(MatchUtils.STR_IPV6_FLOW_LABEL, matchValueToString(m, mf));
 				break;
 			case IPV6_ND_SLL:
-				jGen.writeNumberField(MatchUtils.STR_IPV6_ND_SSL, m.get(MatchField.IPV6_ND_SLL).getLong());
+				jGen.writeStringField(MatchUtils.STR_IPV6_ND_SSL, matchValueToString(m, mf));
 				break;
 			case IPV6_ND_TARGET:
-				jGen.writeNumberField(MatchUtils.STR_IPV6_ND_TARGET, m.get(MatchField.IPV6_ND_TARGET).getZeroCompressStart());
+				jGen.writeStringField(MatchUtils.STR_IPV6_ND_TARGET, matchValueToString(m, mf));
 				break;
 			case IPV6_ND_TLL:
-				jGen.writeNumberField(MatchUtils.STR_IPV6_ND_TTL, m.get(MatchField.IPV6_ND_TLL).getLong());
+				jGen.writeStringField(MatchUtils.STR_IPV6_ND_TTL, matchValueToString(m, mf));
 				break;
 			case METADATA:
-				jGen.writeNumberField(MatchUtils.STR_METADATA, m.get(MatchField.METADATA).getValue().getValue());
+				jGen.writeStringField(MatchUtils.STR_METADATA, matchValueToString(m, mf));
 				break;
 			case MPLS_LABEL:
-				jGen.writeNumberField(MatchUtils.STR_MPLS_LABEL, m.get(MatchField.MPLS_LABEL).getValue());
+				jGen.writeStringField(MatchUtils.STR_MPLS_LABEL, matchValueToString(m, mf));
 				break;
 			case MPLS_TC:
-				jGen.writeNumberField(MatchUtils.STR_MPLS_TC, m.get(MatchField.MPLS_TC).getValue());
+				jGen.writeStringField(MatchUtils.STR_MPLS_TC, matchValueToString(m, mf));
 				break;
 			case MPLS_BOS:
-				jGen.writeStringField(MatchUtils.STR_MPLS_BOS, m.get(MatchField.MPLS_BOS).toString());
+				jGen.writeStringField(MatchUtils.STR_MPLS_BOS, matchValueToString(m, mf));
 				break;
 			case SCTP_SRC:
-				jGen.writeNumberField(MatchUtils.STR_SCTP_SRC, m.get(MatchField.SCTP_SRC).getPort());
+				jGen.writeStringField(MatchUtils.STR_SCTP_SRC, matchValueToString(m, mf));
 				break;
 			case SCTP_DST:
-				jGen.writeNumberField(MatchUtils.STR_SCTP_DST, m.get(MatchField.SCTP_DST).getPort());
+				jGen.writeStringField(MatchUtils.STR_SCTP_DST, matchValueToString(m, mf));
 				break;
 			case TCP_SRC:
-				jGen.writeNumberField(MatchUtils.STR_TCP_SRC, m.get(MatchField.TCP_SRC).getPort());
+				jGen.writeStringField(MatchUtils.STR_TCP_SRC, matchValueToString(m, mf));
 				break;
 			case TCP_DST:
-				jGen.writeNumberField(MatchUtils.STR_TCP_DST, m.get(MatchField.TCP_DST).getPort());
+				jGen.writeStringField(MatchUtils.STR_TCP_DST, matchValueToString(m, mf));
 				break;
 			case UDP_SRC:
-				jGen.writeNumberField(MatchUtils.STR_UDP_SRC, m.get(MatchField.UDP_SRC).getPort());
+				jGen.writeStringField(MatchUtils.STR_UDP_SRC, matchValueToString(m, mf));
 				break;
 			case UDP_DST:
-				jGen.writeNumberField(MatchUtils.STR_UDP_DST, m.get(MatchField.UDP_DST).getPort());
+				jGen.writeStringField(MatchUtils.STR_UDP_DST, matchValueToString(m, mf));
 				break;
 			default:
 				// either a BSN or unknown match type
diff --git a/src/main/java/net/floodlightcontroller/core/web/serializers/OFInstructionListSerializer.java b/src/main/java/net/floodlightcontroller/core/web/serializers/OFInstructionListSerializer.java
index e16b18548..a59d38fd3 100644
--- a/src/main/java/net/floodlightcontroller/core/web/serializers/OFInstructionListSerializer.java
+++ b/src/main/java/net/floodlightcontroller/core/web/serializers/OFInstructionListSerializer.java
@@ -45,20 +45,20 @@ public class OFInstructionListSerializer extends JsonSerializer<List<OFInstructi
                     jGen.writeObjectFieldStart(InstructionUtils.STR_CLEAR_ACTIONS);
                     break;
                 case WRITE_METADATA:
-                    jGen.writeStartObject();
+                    jGen.writeObjectFieldStart(InstructionUtils.STR_WRITE_METADATA);
                     jGen.writeNumberField(InstructionUtils.STR_WRITE_METADATA, ((OFInstructionWriteMetadata)i).getMetadata().getValue());
                     jGen.writeNumberField(InstructionUtils.STR_WRITE_METADATA + "_mask", ((OFInstructionWriteMetadata)i).getMetadataMask().getValue());
                     break;
                 case EXPERIMENTER:
-                    jGen.writeStartObject();
+                    jGen.writeObjectFieldStart(InstructionUtils.STR_EXPERIMENTER);
                     jGen.writeNumberField(InstructionUtils.STR_EXPERIMENTER, ((OFInstructionExperimenter)i).getExperimenter());
                     break;
                 case GOTO_TABLE:
-                    jGen.writeStartObject();
+                    jGen.writeObjectFieldStart(InstructionUtils.STR_GOTO_TABLE);
                     jGen.writeNumberField(InstructionUtils.STR_GOTO_TABLE, ((OFInstructionGotoTable)i).getTableId().getValue());
                     break;
                 case METER:
-                    jGen.writeStartObject();
+                    jGen.writeObjectFieldStart(InstructionUtils.STR_GOTO_METER);
                     jGen.writeNumberField(InstructionUtils.STR_GOTO_METER, ((OFInstructionMeter)i).getMeterId());
                     break;
                 case APPLY_ACTIONS:
diff --git a/src/main/java/net/floodlightcontroller/staticflowentry/IStaticFlowEntryPusherService.java b/src/main/java/net/floodlightcontroller/staticflowentry/IStaticFlowEntryPusherService.java
index 22b60197b..0abf9d2f3 100644
--- a/src/main/java/net/floodlightcontroller/staticflowentry/IStaticFlowEntryPusherService.java
+++ b/src/main/java/net/floodlightcontroller/staticflowentry/IStaticFlowEntryPusherService.java
@@ -39,7 +39,7 @@ public interface IStaticFlowEntryPusherService extends IFloodlightService {
     public void deleteFlow(String name);
     
     /**
-     * Deletes all static flows for a practicular switch
+     * Deletes all static flows for a particular switch
      * @param dpid The DPID of the switch to delete flows for.
      */
     public void deleteFlowsForSwitch(DatapathId dpid);
diff --git a/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntries.java b/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntries.java
index fec72f40a..94a8686af 100644
--- a/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntries.java
+++ b/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntries.java
@@ -17,6 +17,7 @@
 package net.floodlightcontroller.staticflowentry;
 
 import java.io.IOException;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
@@ -37,6 +38,7 @@ import com.fasterxml.jackson.core.JsonToken;
 import com.fasterxml.jackson.databind.MappingJsonFactory;
 
 import org.projectfloodlight.openflow.protocol.OFFlowMod;
+import org.projectfloodlight.openflow.protocol.OFFlowModFlags;
 import org.projectfloodlight.openflow.protocol.instruction.OFInstruction;
 import org.projectfloodlight.openflow.protocol.instruction.OFInstructionApplyActions;
 import org.projectfloodlight.openflow.protocol.instruction.OFInstructionClearActions;
@@ -90,7 +92,8 @@ public class StaticFlowEntries {
 		.setBufferId(OFBufferId.NO_BUFFER)
 		.setOutPort(OFPort.ANY) 
 		.setCookie(computeEntryCookie(0, entryName))
-		.setPriority(Integer.MAX_VALUE);
+		.setPriority(Integer.MAX_VALUE)
+		.setFlags(Collections.singleton(OFFlowModFlags.SEND_FLOW_REM));
 		return;
 	}
 
@@ -144,6 +147,8 @@ public class StaticFlowEntries {
 		entry.put(StaticFlowEntryPusher.COLUMN_SWITCH, sw);
 		entry.put(StaticFlowEntryPusher.COLUMN_ACTIVE, Boolean.toString(true));
 		entry.put(StaticFlowEntryPusher.COLUMN_PRIORITY, Integer.toString(fm.getPriority()));
+		entry.put(StaticFlowEntryPusher.COLUMN_IDLE_TIMEOUT, Integer.toString(fm.getIdleTimeout()));
+		entry.put(StaticFlowEntryPusher.COLUMN_HARD_TIMEOUT, Integer.toString(fm.getHardTimeout()));
 
 		switch (fm.getVersion()) {
 		case OF_10:
@@ -367,9 +372,6 @@ public class StaticFlowEntries {
 
 			String n = jp.getCurrentName();
 			jp.nextToken();
-			if (jp.getText().equals("")) {
-				continue;
-			}
 
 			// Java 7 switch-case on strings automatically checks for (deep) string equality.
 			// IMHO, this makes things easier on the eyes than if, else if, else's, and it
@@ -559,6 +561,7 @@ public class StaticFlowEntries {
 				break;
 			default:
 				log.error("Could not decode field from JSON string: {}", n);
+				break;
 			}  
 		} 
 		
diff --git a/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java b/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java
index ba8a2c5ff..3730a095f 100644
--- a/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java
+++ b/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java
@@ -65,6 +65,11 @@ import org.projectfloodlight.openflow.protocol.OFPortDesc;
 import org.projectfloodlight.openflow.protocol.OFMessage;
 import org.projectfloodlight.openflow.protocol.OFType;
 import org.projectfloodlight.openflow.protocol.OFVersion;
+import org.projectfloodlight.openflow.protocol.ver10.OFFlowRemovedReasonSerializerVer10;
+import org.projectfloodlight.openflow.protocol.ver11.OFFlowRemovedReasonSerializerVer11;
+import org.projectfloodlight.openflow.protocol.ver12.OFFlowRemovedReasonSerializerVer12;
+import org.projectfloodlight.openflow.protocol.ver13.OFFlowRemovedReasonSerializerVer13;
+import org.projectfloodlight.openflow.protocol.ver14.OFFlowRemovedReasonSerializerVer14;
 import org.projectfloodlight.openflow.types.DatapathId;
 import org.projectfloodlight.openflow.types.TableId;
 import org.projectfloodlight.openflow.types.U16;
@@ -132,7 +137,7 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService,
 	public static final String COLUMN_ARP_DHA = MatchUtils.STR_ARP_DHA;
 	public static final String COLUMN_ARP_SPA = MatchUtils.STR_ARP_SPA;
 	public static final String COLUMN_ARP_DPA = MatchUtils.STR_ARP_DPA;
-	
+
 	/* IPv6 related columns */
 	public static final String COLUMN_NW6_SRC = MatchUtils.STR_IPV6_SRC;
 	public static final String COLUMN_NW6_DST = MatchUtils.STR_IPV6_DST;
@@ -154,7 +159,7 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService,
 	/* end newly added matches */
 
 	public static final String COLUMN_ACTIONS = "actions";
-	
+
 	public static final String COLUMN_INSTR_GOTO_TABLE = InstructionUtils.STR_GOTO_TABLE; // instructions are each getting their own column, due to write and apply actions, which themselves contain a variable list of actions
 	public static final String COLUMN_INSTR_WRITE_METADATA = InstructionUtils.STR_WRITE_METADATA;
 	public static final String COLUMN_INSTR_WRITE_ACTIONS = InstructionUtils.STR_WRITE_ACTIONS;
@@ -175,7 +180,7 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService,
 		COLUMN_ICMP_TYPE, COLUMN_ICMP_CODE, 
 		COLUMN_ARP_OPCODE, COLUMN_ARP_SHA, COLUMN_ARP_DHA, 
 		COLUMN_ARP_SPA, COLUMN_ARP_DPA,
-		
+
 		/* IPv6 related matches */
 		COLUMN_NW6_SRC, COLUMN_NW6_DST, COLUMN_ICMP6_TYPE, COLUMN_ICMP6_CODE, 
 		COLUMN_IPV6_FLOW_LABEL, COLUMN_ND_SLL, COLUMN_ND_TLL, COLUMN_ND_TARGET,		
@@ -189,7 +194,7 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService,
 		COLUMN_INSTR_CLEAR_ACTIONS, COLUMN_INSTR_GOTO_METER,
 		COLUMN_INSTR_EXPERIMENTER
 		/* end newly added instructions */
-		};
+	};
 
 	protected IFloodlightProviderService floodlightProviderService;
 	protected IOFSwitchService switchService;
@@ -365,11 +370,11 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService,
 				if (row.get(key) == null) {
 					continue;
 				}
-				
+
 				if (key.equals(COLUMN_SWITCH) || key.equals(COLUMN_NAME) || key.equals("id")) {
 					continue; // already handled
 				}
-				
+
 				if (key.equals(COLUMN_ACTIVE)) {
 					if  (!Boolean.valueOf((String) row.get(COLUMN_ACTIVE))) {
 						log.debug("skipping inactive entry {} for switch {}", entryName, switchName);
@@ -489,17 +494,17 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService,
 				entriesFromStorage.put(dpid, new HashMap<String, OFFlowMod>());
 
 			List<OFMessage> outQueue = new ArrayList<OFMessage>();
-			
+
 			/* For every flow per dpid, decide how to "add" the flow. */
 			for (String entry : entriesToAdd.get(dpid).keySet()) {
 				OFFlowMod newFlowMod = entriesToAdd.get(dpid).get(entry);
 				OFFlowMod oldFlowMod = null;
-				
+
 				String dpidOldFlowMod = entry2dpid.get(entry);
 				if (dpidOldFlowMod != null) {
 					oldFlowMod = entriesFromStorage.get(dpidOldFlowMod).remove(entry);
 				}
-				
+
 				/* Modify, which can be either a Flow MODIFY_STRICT or a Flow DELETE_STRICT with a side of Flow ADD */
 				if (oldFlowMod != null && newFlowMod != null) { 
 					/* MODIFY_STRICT b/c the match is still the same */
@@ -511,7 +516,7 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService,
 						entry2dpid.put(entry, dpid);
 						newFlowMod = FlowModUtils.toFlowModifyStrict(newFlowMod);
 						outQueue.add(newFlowMod);
-					/* DELETE_STRICT and then ADD b/c the match is now different */
+						/* DELETE_STRICT and then ADD b/c the match is now different */
 					} else {
 						log.debug("DeleteStrict and Add SFP Flow");
 						oldFlowMod = FlowModUtils.toFlowDeleteStrict(oldFlowMod);
@@ -520,7 +525,7 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService,
 						if (dpidOldFlowMod.equals(dpid)) {
 							outQueue.add(oldFlowMod);
 							outQueue.add(addTmp); 
-						/* Otherwise, go ahead and send the flows now (since queuing them will send to the wrong switch). */
+							/* Otherwise, go ahead and send the flows now (since queuing them will send to the wrong switch). */
 						} else {
 							writeOFMessageToSwitch(DatapathId.of(dpidOldFlowMod), oldFlowMod);
 							writeOFMessageToSwitch(DatapathId.of(dpid), FlowModUtils.toFlowAdd(newFlowMod)); 
@@ -528,14 +533,14 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService,
 						entriesFromStorage.get(dpid).put(entry, addTmp);
 						entry2dpid.put(entry, dpid);			
 					}
-				/* Add a brand-new flow with ADD */
+					/* Add a brand-new flow with ADD */
 				} else if (newFlowMod != null && oldFlowMod == null) {
 					log.debug("Add SFP Flow");
 					OFFlowAdd addTmp = FlowModUtils.toFlowAdd(newFlowMod);
 					entriesFromStorage.get(dpid).put(entry, addTmp);
 					entry2dpid.put(entry, dpid);
 					outQueue.add(addTmp);
-				/* Something strange happened, so remove the flow */
+					/* Something strange happened, so remove the flow */
 				} else if (newFlowMod == null) { 
 					entriesFromStorage.get(dpid).remove(entry);
 					entry2dpid.remove(entry);
@@ -666,7 +671,6 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService,
 		sw.write(flowMod);
 		sw.flush();
 	}
-
 	@Override
 	public String getName() {
 		return StaticFlowName;
@@ -688,13 +692,74 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService,
 		 * never expire.
 		 */
 		if (AppCookie.extractApp(cookie) == STATIC_FLOW_APP_ID) {
-			if (OFFlowRemovedReason.DELETE.equals(msg.getReason()))
-				log.error("Got a FlowRemove message for a infinite " +
-						"timeout flow: {} from switch {}", msg, sw);
-			// Stop the processing chain since we sent the delete.
-			return Command.STOP;
+			OFFlowRemovedReason reason = null;
+			switch (msg.getVersion()) {
+			case OF_10:
+				reason = OFFlowRemovedReasonSerializerVer10.ofWireValue((byte) msg.getReason());
+				break;
+			case OF_11:
+				reason = OFFlowRemovedReasonSerializerVer11.ofWireValue((byte) msg.getReason());
+				break;
+			case OF_12:
+				reason = OFFlowRemovedReasonSerializerVer12.ofWireValue((byte) msg.getReason());
+				break;
+			case OF_13:
+				reason = OFFlowRemovedReasonSerializerVer13.ofWireValue((byte) msg.getReason());
+				break;
+			case OF_14:
+				reason = OFFlowRemovedReasonSerializerVer14.ofWireValue((byte) msg.getReason());
+				break;
+			default:
+				log.debug("OpenFlow version {} unsupported for OFFlowRemovedReasonSerializerVerXX", msg.getVersion());
+				break;
+			}
+			if (reason != null) {
+				if (OFFlowRemovedReason.DELETE == reason) {
+					log.error("Got a FlowRemove message for a infinite " + 
+							"timeout flow: {} from switch {}", msg, sw);
+				} else if (OFFlowRemovedReason.HARD_TIMEOUT == reason || OFFlowRemovedReason.IDLE_TIMEOUT == reason) {
+					/* Remove the Flow from the DB since it timed out */
+					log.debug("Received an IDLE or HARD timeout for an SFP flow. Removing it from the SFP DB.");
+					/* 
+					 * Lookup the flow based on the flow contents. We do not know/care about the name of the 
+					 * flow based on this message, but we can get the table values for this switch and search.
+					 */
+					String flowToRemove = null;
+					Map<String, OFFlowMod> flowsByName = getFlows(sw.getId());
+					for (Map.Entry<String, OFFlowMod> entry : flowsByName.entrySet()) {
+						if (msg.getCookie().equals(entry.getValue().getCookie()) &&
+								msg.getHardTimeout() == entry.getValue().getHardTimeout() &&
+								msg.getIdleTimeout() == entry.getValue().getIdleTimeout() &&
+								msg.getMatch().equals(entry.getValue().getMatch()) &&
+								msg.getPriority() == entry.getValue().getPriority() &&
+								msg.getTableId().equals(entry.getValue().getTableId())
+							) {
+							flowToRemove = entry.getKey();
+							break;
+						}
+					}
+					
+					log.debug("Flow to Remove: {}", flowToRemove);
+					
+					/*
+					 * Remove the flow. This will send the delete message to the switch,
+					 * since we cannot tell the storage listener rowsdeleted() that we
+					 * are only removing our local DB copy of the flow and that it actually
+					 * timed out on the switch and is already gone. The switch will silently
+					 * discard the delete message in this case.
+					 * 
+					 * TODO: We should come up with a way to convey to the storage listener
+					 * the reason for the flow being removed.
+					 */
+					if (flowToRemove != null) {
+						deleteFlow(flowToRemove);
+					}
+				}
+				/* Stop the processing chain since we sent or asked for the delete message. */
+				return Command.STOP;
+			}
 		}
-
+		/* Continue the processing chain, since we did not send the delete. */
 		return Command.CONTINUE;
 	}
 
@@ -787,7 +852,7 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService,
 			storageSourceService.insertRowAsync(TABLE_NAME, fmMap);
 		} catch (Exception e) {
 			log.error("Error! Check the fields specified for the flow.Make sure IPv4 fields are not mixed with IPv6 fields or all "
-            		+ "mandatory fields are specified. ");
+					+ "mandatory fields are specified. ");
 		}
 	}
 
diff --git a/src/main/java/net/floodlightcontroller/util/InstructionUtils.java b/src/main/java/net/floodlightcontroller/util/InstructionUtils.java
index 1de337103..aa89c1b58 100644
--- a/src/main/java/net/floodlightcontroller/util/InstructionUtils.java
+++ b/src/main/java/net/floodlightcontroller/util/InstructionUtils.java
@@ -278,7 +278,7 @@ public class InstructionUtils {
 			return;
 		}
 
-		if (inst != null && inst.isEmpty()) {
+		if (inst != null && inst.trim().isEmpty()) { /* Allow the empty string, since this is what specifies clear (i.e. key clear does not have any defined values). */
 			OFInstructionClearActions i = OFFactories.getFactory(fmb.getVersion()).instructions().clearActions();
 			log.debug("Appending ClearActions instruction: {}", i);
 			appendInstruction(fmb, i);
diff --git a/src/main/java/net/floodlightcontroller/util/MatchUtils.java b/src/main/java/net/floodlightcontroller/util/MatchUtils.java
index 9b16fa7a8..969356169 100644
--- a/src/main/java/net/floodlightcontroller/util/MatchUtils.java
+++ b/src/main/java/net/floodlightcontroller/util/MatchUtils.java
@@ -11,9 +11,7 @@ import org.projectfloodlight.openflow.types.ArpOpcode;
 import org.projectfloodlight.openflow.types.EthType;
 import org.projectfloodlight.openflow.types.ICMPv4Code;
 import org.projectfloodlight.openflow.types.ICMPv4Type;
-import org.projectfloodlight.openflow.types.IPv4Address;
 import org.projectfloodlight.openflow.types.IPv4AddressWithMask;
-import org.projectfloodlight.openflow.types.IPv6Address;
 import org.projectfloodlight.openflow.types.IPv6AddressWithMask;
 import org.projectfloodlight.openflow.types.IPv6FlowLabel;
 import org.projectfloodlight.openflow.types.IpDscp;
@@ -388,19 +386,33 @@ public class MatchUtils {
 
 			switch (key_value[0]) {
 			case STR_IN_PORT:
-				mb.setExact(MatchField.IN_PORT, OFPort.of(Integer.valueOf(key_value[1])));
+				if (dataMask.length == 1) {
+					mb.setExact(MatchField.IN_PORT, OFPort.of(dataMask[0].contains("0x") ? Integer.valueOf(dataMask[0].replaceFirst("0x", ""), 16) : Integer.valueOf(dataMask[0])));
+				} else {
+					mb.setMasked(MatchField.IN_PORT, OFPort.of(dataMask[0].contains("0x") ? Integer.valueOf(dataMask[0].replaceFirst("0x", ""), 16) : Integer.valueOf(dataMask[0])), 
+					OFPort.of(dataMask[1].contains("0x") ? Integer.valueOf(dataMask[1].replaceFirst("0x", ""), 16) : Integer.valueOf(dataMask[1])));
+				}
 				break;
-			case STR_DL_DST:
-				mb.setExact(MatchField.ETH_DST, MacAddress.of(key_value[1]));
+			case STR_DL_DST: /* Only accept hex-string for MAC addresses */
+				if (dataMask.length == 1) {
+					mb.setExact(MatchField.ETH_DST, MacAddress.of(dataMask[0]));
+				} else {
+					mb.setMasked(MatchField.ETH_DST, MacAddress.of(dataMask[0]), MacAddress.of(dataMask[1]));
+				}
 				break;
 			case STR_DL_SRC:
-				mb.setExact(MatchField.ETH_SRC, MacAddress.of(key_value[1]));
+				if (dataMask.length == 1) {
+					mb.setExact(MatchField.ETH_SRC, MacAddress.of(dataMask[0]));
+				} else {
+					mb.setMasked(MatchField.ETH_SRC, MacAddress.of(dataMask[0]), MacAddress.of(dataMask[1]));
+				}
 				break;
 			case STR_DL_TYPE:
-				if (key_value[1].startsWith("0x")) {
-					mb.setExact(MatchField.ETH_TYPE, EthType.of(Integer.valueOf(key_value[1].replaceFirst("0x", ""), 16)));
+				if (dataMask.length == 1) {
+					mb.setExact(MatchField.ETH_TYPE, EthType.of(dataMask[0].contains("0x") ? Integer.valueOf(dataMask[0].replaceFirst("0x", ""), 16) : Integer.valueOf(dataMask[0])));
 				} else {
-					mb.setExact(MatchField.ETH_TYPE, EthType.of(Integer.valueOf(key_value[1])));
+					mb.setMasked(MatchField.ETH_TYPE, EthType.of(dataMask[0].contains("0x") ? Integer.valueOf(dataMask[0].replaceFirst("0x", ""), 16) : Integer.valueOf(dataMask[0])), 
+					EthType.of(dataMask[1].contains("0x") ? Integer.valueOf(dataMask[1].replaceFirst("0x", ""), 16) : Integer.valueOf(dataMask[1])));
 				}
 				break;
 			case STR_DL_VLAN:
@@ -413,19 +425,20 @@ public class MatchUtils {
 				}
 				break;
 			case STR_DL_VLAN_PCP:
-				if (key_value[1].startsWith("0x")) { 
-					mb.setExact(MatchField.VLAN_PCP, VlanPcp.of(U8.t(Short.valueOf(key_value[1].replaceFirst("0x", ""), 16))));
+				if (dataMask.length == 1) {
+					mb.setExact(MatchField.VLAN_PCP, VlanPcp.of(dataMask[0].contains("0x") ? U8.t(Short.valueOf(dataMask[0].replaceFirst("0x", ""), 16)) : U8.t(Short.valueOf(dataMask[0]))));
 				} else {
-					mb.setExact(MatchField.VLAN_PCP, VlanPcp.of(U8.t(Short.valueOf(key_value[1]))));
+					mb.setMasked(MatchField.VLAN_PCP, VlanPcp.of(dataMask[0].contains("0x") ? U8.t(Short.valueOf(dataMask[0].replaceFirst("0x", ""), 16)) : U8.t(Short.valueOf(dataMask[0]))), 
+					VlanPcp.of(dataMask[1].contains("0x") ? U8.t(Short.valueOf(dataMask[1].replaceFirst("0x", ""), 16)) : U8.t(Short.valueOf(dataMask[1]))));
 				}
 				break;
-			case STR_NW_DST:
+			case STR_NW_DST: /* Only accept dotted-decimal for IPv4 addresses */
 				mb.setMasked(MatchField.IPV4_DST, IPv4AddressWithMask.of(key_value[1]));
 				break;
 			case STR_NW_SRC:
 				mb.setMasked(MatchField.IPV4_SRC, IPv4AddressWithMask.of(key_value[1]));
 				break;
-			case STR_IPV6_DST:
+			case STR_IPV6_DST: /* Only accept hex-string for IPv6 addresses */
 				if (ver10 == true) {
 					throw new IllegalArgumentException("OF Version incompatible");
 				}
@@ -441,207 +454,297 @@ public class MatchUtils {
 				if (ver10 == true) {
 					throw new IllegalArgumentException("OF Version incompatible");
 				}
-				if (key_value[1].startsWith("0x")) { 
-					mb.setExact(MatchField.IPV6_FLABEL, IPv6FlowLabel.of(Integer.parseInt(key_value[1].replaceFirst("0x", ""), 16)));
+				if (dataMask.length == 1) {
+					mb.setExact(MatchField.IPV6_FLABEL, IPv6FlowLabel.of(dataMask[0].contains("0x") ? Integer.valueOf(dataMask[0].replaceFirst("0x", ""), 16) : Integer.valueOf(dataMask[0])));
 				} else {
-					mb.setExact(MatchField.IPV6_FLABEL, IPv6FlowLabel.of(Integer.parseInt(key_value[1])));
+					mb.setMasked(MatchField.IPV6_FLABEL, IPv6FlowLabel.of(dataMask[0].contains("0x") ? Integer.valueOf(dataMask[0].replaceFirst("0x", ""), 16) : Integer.valueOf(dataMask[0])), 
+					IPv6FlowLabel.of(dataMask[1].contains("0x") ? Integer.valueOf(dataMask[1].replaceFirst("0x", ""), 16) : Integer.valueOf(dataMask[1])));
 				}
 				break;
 			case STR_NW_PROTO:
-				if (key_value[1].startsWith("0x")) {
-					mb.setExact(MatchField.IP_PROTO, IpProtocol.of(Short.valueOf(key_value[1].replaceFirst("0x", ""), 16)));
+				if (dataMask.length == 1) {
+					mb.setExact(MatchField.IP_PROTO, IpProtocol.of(dataMask[0].contains("0x") ? Short.valueOf(dataMask[0].replaceFirst("0x", ""), 16) : Short.valueOf(dataMask[0])));
 				} else {
-					mb.setExact(MatchField.IP_PROTO, IpProtocol.of(Short.valueOf(key_value[1])));
+					mb.setMasked(MatchField.IP_PROTO, IpProtocol.of(dataMask[0].contains("0x") ? Short.valueOf(dataMask[0].replaceFirst("0x", ""), 16) : Short.valueOf(dataMask[0])), 
+					IpProtocol.of(dataMask[1].contains("0x") ? Short.valueOf(dataMask[1].replaceFirst("0x", ""), 16) : Short.valueOf(dataMask[1])));
 				}
 				break;
 			case STR_NW_TOS:
-				if (key_value[1].startsWith("0x")) {
-					mb.setExact(MatchField.IP_ECN, IpEcn.of(U8.t(Short.valueOf(key_value[1].replaceFirst("0x", ""), 16))));
-					mb.setExact(MatchField.IP_DSCP, IpDscp.of(U8.t(Short.valueOf(key_value[1].replaceFirst("0x", ""), 16))));
+				if (dataMask.length == 1) {
+					mb.setExact(MatchField.IP_ECN, IpEcn.of(dataMask[0].contains("0x") ? U8.t(Short.valueOf(dataMask[0].replaceFirst("0x", ""), 16)) : U8.t(Short.valueOf(dataMask[0]))));
+					mb.setExact(MatchField.IP_DSCP, IpDscp.of(dataMask[0].contains("0x") ? U8.t(Short.valueOf(dataMask[0].replaceFirst("0x", ""), 16)) : U8.t(Short.valueOf(dataMask[0]))));
 				} else {
-					mb.setExact(MatchField.IP_ECN, IpEcn.of(U8.t(Short.valueOf(key_value[1]))));
-					mb.setExact(MatchField.IP_DSCP, IpDscp.of(U8.t(Short.valueOf(key_value[1]))));
+					mb.setMasked(MatchField.IP_ECN, IpEcn.of(dataMask[0].contains("0x") ? U8.t(Short.valueOf(dataMask[0].replaceFirst("0x", ""), 16)) : U8.t(Short.valueOf(dataMask[0]))), 
+							IpEcn.of(dataMask[1].contains("0x") ? U8.t(Short.valueOf(dataMask[1].replaceFirst("0x", ""), 16)) : U8.t(Short.valueOf(dataMask[1]))));
+					mb.setMasked(MatchField.IP_DSCP, IpDscp.of(dataMask[0].contains("0x") ? U8.t(Short.valueOf(dataMask[0].replaceFirst("0x", ""), 16)) : U8.t(Short.valueOf(dataMask[0]))), 
+							IpDscp.of(dataMask[1].contains("0x") ? U8.t(Short.valueOf(dataMask[1].replaceFirst("0x", ""), 16)) : U8.t(Short.valueOf(dataMask[1]))));
 				}
 				break;
 			case STR_NW_ECN:
-				if (key_value[1].startsWith("0x")) {
-					mb.setExact(MatchField.IP_ECN, IpEcn.of(U8.t(Short.valueOf(key_value[1].replaceFirst("0x", ""), 16))));
+				if (dataMask.length == 1) {
+					mb.setExact(MatchField.IP_ECN, IpEcn.of(dataMask[0].contains("0x") ? U8.t(Short.valueOf(dataMask[0].replaceFirst("0x", ""), 16)) : U8.t(Short.valueOf(dataMask[0]))));
 				} else {
-					mb.setExact(MatchField.IP_ECN, IpEcn.of(U8.t(Short.valueOf(key_value[1]))));
+					mb.setMasked(MatchField.IP_ECN, IpEcn.of(dataMask[0].contains("0x") ? U8.t(Short.valueOf(dataMask[0].replaceFirst("0x", ""), 16)) : U8.t(Short.valueOf(dataMask[0]))), 
+							IpEcn.of(dataMask[1].contains("0x") ? U8.t(Short.valueOf(dataMask[1].replaceFirst("0x", ""), 16)) : U8.t(Short.valueOf(dataMask[1]))));
 				}
 				break;
 			case STR_NW_DSCP:
-				if (key_value[1].startsWith("0x")) {
-					mb.setExact(MatchField.IP_DSCP, IpDscp.of(U8.t(Short.valueOf(key_value[1].replaceFirst("0x", ""), 16))));
+				if (dataMask.length == 1) {
+					mb.setExact(MatchField.IP_DSCP, IpDscp.of(dataMask[0].contains("0x") ? U8.t(Short.valueOf(dataMask[0].replaceFirst("0x", ""), 16)) : U8.t(Short.valueOf(dataMask[0]))));
 				} else {
-					mb.setExact(MatchField.IP_DSCP, IpDscp.of(U8.t(Short.valueOf(key_value[1]))));
+					mb.setMasked(MatchField.IP_DSCP, IpDscp.of(dataMask[0].contains("0x") ? U8.t(Short.valueOf(dataMask[0].replaceFirst("0x", ""), 16)) : U8.t(Short.valueOf(dataMask[0]))), 
+							IpDscp.of(dataMask[1].contains("0x") ? U8.t(Short.valueOf(dataMask[1].replaceFirst("0x", ""), 16)) : U8.t(Short.valueOf(dataMask[1]))));
 				}
 				break;
 			case STR_SCTP_DST: // for transport ports, if we don't know the transport protocol yet, postpone parsing this [key, value] pair until we know. Put it at the back of the queue.
 				if (mb.get(MatchField.IP_PROTO) == null) {
 					llValues.add(key_value); // place it back if we can't proceed yet
 				} else {
-					mb.setExact(MatchField.SCTP_DST, TransportPort.of(Integer.valueOf(key_value[1])));
+					if (dataMask.length == 1) {
+						mb.setExact(MatchField.SCTP_DST, TransportPort.of(dataMask[0].contains("0x") ? Integer.valueOf(dataMask[0].replaceFirst("0x", ""), 16) : Integer.valueOf(dataMask[0])));
+					} else {
+						mb.setMasked(MatchField.SCTP_DST, TransportPort.of(dataMask[0].contains("0x") ? Integer.valueOf(dataMask[0].replaceFirst("0x", ""), 16) : Integer.valueOf(dataMask[0])), 
+								TransportPort.of(dataMask[1].contains("0x") ? Integer.valueOf(dataMask[1].replaceFirst("0x", ""), 16) : Integer.valueOf(dataMask[1])));
+					}
 				}
 				break;
 			case STR_SCTP_SRC:
 				if (mb.get(MatchField.IP_PROTO) == null) {
 					llValues.add(key_value); // place it back if we can't proceed yet
 				} else {
-					mb.setExact(MatchField.SCTP_SRC, TransportPort.of(Integer.valueOf(key_value[1])));
+					if (dataMask.length == 1) {
+						mb.setExact(MatchField.SCTP_SRC, TransportPort.of(dataMask[0].contains("0x") ? Integer.valueOf(dataMask[0].replaceFirst("0x", ""), 16) : Integer.valueOf(dataMask[0])));
+					} else {
+						mb.setMasked(MatchField.SCTP_SRC, TransportPort.of(dataMask[0].contains("0x") ? Integer.valueOf(dataMask[0].replaceFirst("0x", ""), 16) : Integer.valueOf(dataMask[0])), 
+								TransportPort.of(dataMask[1].contains("0x") ? Integer.valueOf(dataMask[1].replaceFirst("0x", ""), 16) : Integer.valueOf(dataMask[1])));
+					}
 				}
 				break;
 			case STR_UDP_DST:
 				if (mb.get(MatchField.IP_PROTO) == null) {
 					llValues.add(key_value); // place it back if we can't proceed yet
 				} else {
-					mb.setExact(MatchField.UDP_DST, TransportPort.of(Integer.valueOf(key_value[1])));
+					if (dataMask.length == 1) {
+						mb.setExact(MatchField.UDP_DST, TransportPort.of(dataMask[0].contains("0x") ? Integer.valueOf(dataMask[0].replaceFirst("0x", ""), 16) : Integer.valueOf(dataMask[0])));
+					} else {
+						mb.setMasked(MatchField.UDP_DST, TransportPort.of(dataMask[0].contains("0x") ? Integer.valueOf(dataMask[0].replaceFirst("0x", ""), 16) : Integer.valueOf(dataMask[0])), 
+								TransportPort.of(dataMask[1].contains("0x") ? Integer.valueOf(dataMask[1].replaceFirst("0x", ""), 16) : Integer.valueOf(dataMask[1])));
+					}
 				}
 				break;
 			case STR_UDP_SRC:
 				if (mb.get(MatchField.IP_PROTO) == null) {
 					llValues.add(key_value); // place it back if we can't proceed yet
 				} else {
-					mb.setExact(MatchField.UDP_SRC, TransportPort.of(Integer.valueOf(key_value[1])));
+					if (dataMask.length == 1) {
+						mb.setExact(MatchField.UDP_SRC, TransportPort.of(dataMask[0].contains("0x") ? Integer.valueOf(dataMask[0].replaceFirst("0x", ""), 16) : Integer.valueOf(dataMask[0])));
+					} else {
+						mb.setMasked(MatchField.UDP_SRC, TransportPort.of(dataMask[0].contains("0x") ? Integer.valueOf(dataMask[0].replaceFirst("0x", ""), 16) : Integer.valueOf(dataMask[0])), 
+								TransportPort.of(dataMask[1].contains("0x") ? Integer.valueOf(dataMask[1].replaceFirst("0x", ""), 16) : Integer.valueOf(dataMask[1])));
+					}
 				}
 				break;
 			case STR_TCP_DST:
 				if (mb.get(MatchField.IP_PROTO) == null) {
 					llValues.add(key_value); // place it back if we can't proceed yet
 				} else {
-					mb.setExact(MatchField.TCP_DST, TransportPort.of(Integer.valueOf(key_value[1])));
+					if (dataMask.length == 1) {
+						mb.setExact(MatchField.TCP_DST, TransportPort.of(dataMask[0].contains("0x") ? Integer.valueOf(dataMask[0].replaceFirst("0x", ""), 16) : Integer.valueOf(dataMask[0])));
+					} else {
+						mb.setMasked(MatchField.TCP_DST, TransportPort.of(dataMask[0].contains("0x") ? Integer.valueOf(dataMask[0].replaceFirst("0x", ""), 16) : Integer.valueOf(dataMask[0])), 
+								TransportPort.of(dataMask[1].contains("0x") ? Integer.valueOf(dataMask[1].replaceFirst("0x", ""), 16) : Integer.valueOf(dataMask[1])));
+					}
 				}
 				break;
 			case STR_TCP_SRC:
 				if (mb.get(MatchField.IP_PROTO) == null) {
 					llValues.add(key_value); // place it back if we can't proceed yet
 				} else {
-					mb.setExact(MatchField.TCP_SRC, TransportPort.of(Integer.valueOf(key_value[1])));
+					if (dataMask.length == 1) {
+						mb.setExact(MatchField.TCP_SRC, TransportPort.of(dataMask[0].contains("0x") ? Integer.valueOf(dataMask[0].replaceFirst("0x", ""), 16) : Integer.valueOf(dataMask[0])));
+					} else {
+						mb.setMasked(MatchField.TCP_SRC, TransportPort.of(dataMask[0].contains("0x") ? Integer.valueOf(dataMask[0].replaceFirst("0x", ""), 16) : Integer.valueOf(dataMask[0])), 
+								TransportPort.of(dataMask[1].contains("0x") ? Integer.valueOf(dataMask[1].replaceFirst("0x", ""), 16) : Integer.valueOf(dataMask[1])));
+					}
 				}
 				break;
 			case STR_TP_DST: // support for OF1.0 generic transport ports
 				if ((ipProto = mb.get(MatchField.IP_PROTO)) == null) {
 					llValues.add(key_value); // place it back if we can't proceed yet
 				} else if (ipProto == IpProtocol.TCP){
-					mb.setExact(MatchField.TCP_DST, TransportPort.of(Integer.valueOf(key_value[1])));
+					if (dataMask.length == 1) {
+						mb.setExact(MatchField.TCP_DST, TransportPort.of(dataMask[0].contains("0x") ? Integer.valueOf(dataMask[0].replaceFirst("0x", ""), 16) : Integer.valueOf(dataMask[0])));
+					} else {
+						mb.setMasked(MatchField.TCP_DST, TransportPort.of(dataMask[0].contains("0x") ? Integer.valueOf(dataMask[0].replaceFirst("0x", ""), 16) : Integer.valueOf(dataMask[0])), 
+								TransportPort.of(dataMask[1].contains("0x") ? Integer.valueOf(dataMask[1].replaceFirst("0x", ""), 16) : Integer.valueOf(dataMask[1])));
+					}
 				} else if (ipProto == IpProtocol.UDP){
-					mb.setExact(MatchField.UDP_DST, TransportPort.of(Integer.valueOf(key_value[1])));
+					if (dataMask.length == 1) {
+						mb.setExact(MatchField.UDP_DST, TransportPort.of(dataMask[0].contains("0x") ? Integer.valueOf(dataMask[0].replaceFirst("0x", ""), 16) : Integer.valueOf(dataMask[0])));
+					} else {
+						mb.setMasked(MatchField.UDP_DST, TransportPort.of(dataMask[0].contains("0x") ? Integer.valueOf(dataMask[0].replaceFirst("0x", ""), 16) : Integer.valueOf(dataMask[0])), 
+								TransportPort.of(dataMask[1].contains("0x") ? Integer.valueOf(dataMask[1].replaceFirst("0x", ""), 16) : Integer.valueOf(dataMask[1])));
+					}
 				} else if (ipProto == IpProtocol.SCTP){
-					mb.setExact(MatchField.SCTP_DST, TransportPort.of(Integer.valueOf(key_value[1])));
+					if (dataMask.length == 1) {
+						mb.setExact(MatchField.SCTP_DST, TransportPort.of(dataMask[0].contains("0x") ? Integer.valueOf(dataMask[0].replaceFirst("0x", ""), 16) : Integer.valueOf(dataMask[0])));
+					} else {
+						mb.setMasked(MatchField.SCTP_DST, TransportPort.of(dataMask[0].contains("0x") ? Integer.valueOf(dataMask[0].replaceFirst("0x", ""), 16) : Integer.valueOf(dataMask[0])), 
+								TransportPort.of(dataMask[1].contains("0x") ? Integer.valueOf(dataMask[1].replaceFirst("0x", ""), 16) : Integer.valueOf(dataMask[1])));
+					}
 				}
 				break;
 			case STR_TP_SRC:
 				if ((ipProto = mb.get(MatchField.IP_PROTO)) == null) {
 					llValues.add(key_value); // place it back if we can't proceed yet
 				}  else if (ipProto == IpProtocol.TCP){
-					mb.setExact(MatchField.TCP_SRC, TransportPort.of(Integer.valueOf(key_value[1])));
+					if (dataMask.length == 1) {
+						mb.setExact(MatchField.TCP_SRC, TransportPort.of(dataMask[0].contains("0x") ? Integer.valueOf(dataMask[0].replaceFirst("0x", ""), 16) : Integer.valueOf(dataMask[0])));
+					} else {
+						mb.setMasked(MatchField.TCP_SRC, TransportPort.of(dataMask[0].contains("0x") ? Integer.valueOf(dataMask[0].replaceFirst("0x", ""), 16) : Integer.valueOf(dataMask[0])), 
+								TransportPort.of(dataMask[1].contains("0x") ? Integer.valueOf(dataMask[1].replaceFirst("0x", ""), 16) : Integer.valueOf(dataMask[1])));
+					}
 				} else if (ipProto == IpProtocol.UDP){
-					mb.setExact(MatchField.UDP_SRC, TransportPort.of(Integer.valueOf(key_value[1])));
+					if (dataMask.length == 1) {
+						mb.setExact(MatchField.UDP_SRC, TransportPort.of(dataMask[0].contains("0x") ? Integer.valueOf(dataMask[0].replaceFirst("0x", ""), 16) : Integer.valueOf(dataMask[0])));
+					} else {
+						mb.setMasked(MatchField.UDP_SRC, TransportPort.of(dataMask[0].contains("0x") ? Integer.valueOf(dataMask[0].replaceFirst("0x", ""), 16) : Integer.valueOf(dataMask[0])), 
+								TransportPort.of(dataMask[1].contains("0x") ? Integer.valueOf(dataMask[1].replaceFirst("0x", ""), 16) : Integer.valueOf(dataMask[1])));
+					}
 				} else if (ipProto == IpProtocol.SCTP){
-					mb.setExact(MatchField.SCTP_SRC, TransportPort.of(Integer.valueOf(key_value[1])));
+					if (dataMask.length == 1) {
+						mb.setExact(MatchField.SCTP_SRC, TransportPort.of(dataMask[0].contains("0x") ? Integer.valueOf(dataMask[0].replaceFirst("0x", ""), 16) : Integer.valueOf(dataMask[0])));
+					} else {
+						mb.setMasked(MatchField.SCTP_SRC, TransportPort.of(dataMask[0].contains("0x") ? Integer.valueOf(dataMask[0].replaceFirst("0x", ""), 16) : Integer.valueOf(dataMask[0])), 
+								TransportPort.of(dataMask[1].contains("0x") ? Integer.valueOf(dataMask[1].replaceFirst("0x", ""), 16) : Integer.valueOf(dataMask[1])));
+					}
 				}
 				break;
 			case STR_ICMP_TYPE:
-				if (key_value[1].startsWith("0x")) {
-					mb.setExact(MatchField.ICMPV4_TYPE, ICMPv4Type.of(Short.parseShort(key_value[1].replaceFirst("0x", ""), 16)));
+				if (dataMask.length == 1) {
+					mb.setExact(MatchField.ICMPV4_TYPE, ICMPv4Type.of(dataMask[0].contains("0x") ? Short.valueOf(dataMask[0].replaceFirst("0x", ""), 16) : Short.valueOf(dataMask[0])));
 				} else {
-					mb.setExact(MatchField.ICMPV4_TYPE, ICMPv4Type.of(Short.parseShort(key_value[1])));
+					mb.setMasked(MatchField.ICMPV4_TYPE, ICMPv4Type.of(dataMask[0].contains("0x") ? Short.valueOf(dataMask[0].replaceFirst("0x", ""), 16) : Short.valueOf(dataMask[0])), 
+							ICMPv4Type.of(dataMask[1].contains("0x") ? Short.valueOf(dataMask[1].replaceFirst("0x", ""), 16) : Short.valueOf(dataMask[1])));
 				}
 				break;
 			case STR_ICMP_CODE:
-				if (key_value[1].startsWith("0x")) {
-					mb.setExact(MatchField.ICMPV4_CODE, ICMPv4Code.of(Short.parseShort(key_value[1].replaceFirst("0x", ""), 16)));
+				if (dataMask.length == 1) {
+					mb.setExact(MatchField.ICMPV4_CODE, ICMPv4Code.of(dataMask[0].contains("0x") ? Short.valueOf(dataMask[0].replaceFirst("0x", ""), 16) : Short.valueOf(dataMask[0])));
 				} else {
-					mb.setExact(MatchField.ICMPV4_CODE, ICMPv4Code.of(Short.parseShort(key_value[1])));
+					mb.setMasked(MatchField.ICMPV4_CODE, ICMPv4Code.of(dataMask[0].contains("0x") ? Short.valueOf(dataMask[0].replaceFirst("0x", ""), 16) : Short.valueOf(dataMask[0])), 
+							ICMPv4Code.of(dataMask[1].contains("0x") ? Short.valueOf(dataMask[1].replaceFirst("0x", ""), 16) : Short.valueOf(dataMask[1])));
 				}
 				break;
-
-				//sanjivini
 			case STR_ICMPV6_TYPE:
 				if (ver10 == true) {
 					throw new IllegalArgumentException("OF Version incompatible");
-					//throw new Exception("OF Version incompatible");
 				}
-				mb.setExact(MatchField.ICMPV6_TYPE, U8.of(Short.parseShort(key_value[1])));
+				if (dataMask.length == 1) {
+					mb.setExact(MatchField.ICMPV6_TYPE, dataMask[0].contains("0x") ? U8.of(Short.valueOf(dataMask[0].replaceFirst("0x", ""), 16)) : U8.of(Short.valueOf(dataMask[0])));
+				} else {
+					mb.setMasked(MatchField.ICMPV6_TYPE, dataMask[0].contains("0x") ? U8.of(Short.valueOf(dataMask[0].replaceFirst("0x", ""), 16)) : U8.of(Short.valueOf(dataMask[0])), 
+							dataMask[1].contains("0x") ? U8.of(Short.valueOf(dataMask[1].replaceFirst("0x", ""), 16)) : U8.of(Short.valueOf(dataMask[1])));
+				}
 				break;
 			case STR_ICMPV6_CODE:
 				if (ver10 == true) {
 					throw new IllegalArgumentException("OF Version incompatible");
-					//throw new Exception("OF Version incompatible");
 				}
-				mb.setExact(MatchField.ICMPV6_CODE, U8.of(Short.parseShort(key_value[1])));
+				if (dataMask.length == 1) {
+					mb.setExact(MatchField.ICMPV6_CODE, dataMask[0].contains("0x") ? U8.of(Short.valueOf(dataMask[0].replaceFirst("0x", ""), 16)) : U8.of(Short.valueOf(dataMask[0])));
+				} else {
+					mb.setMasked(MatchField.ICMPV6_CODE, dataMask[0].contains("0x") ? U8.of(Short.valueOf(dataMask[0].replaceFirst("0x", ""), 16)) : U8.of(Short.valueOf(dataMask[0])), 
+							dataMask[1].contains("0x") ? U8.of(Short.valueOf(dataMask[1].replaceFirst("0x", ""), 16)) : U8.of(Short.valueOf(dataMask[1])));
+				}
 				break;
 			case STR_IPV6_ND_SSL:
 				if (ver10 == true) {
 					throw new IllegalArgumentException("OF Version incompatible");
-					//throw new Exception("OF Version incompatible");
 				}
-				mb.setExact(MatchField.IPV6_ND_SLL, MacAddress.of(key_value[1]));
+				if (dataMask.length == 1) {
+					mb.setExact(MatchField.IPV6_ND_SLL, MacAddress.of(dataMask[0]));
+				} else {
+					mb.setMasked(MatchField.IPV6_ND_SLL, MacAddress.of(dataMask[0]), MacAddress.of(dataMask[1]));
+				}
 				break;
 			case STR_IPV6_ND_TTL:
 				if (ver10 == true) {
 					throw new IllegalArgumentException("OF Version incompatible");
-					//throw new Exception("OF Version incompatible");
 				}
-				mb.setExact(MatchField.IPV6_ND_TLL, MacAddress.of(key_value[1]));
+				if (dataMask.length == 1) {
+					mb.setExact(MatchField.IPV6_ND_TLL, MacAddress.of(dataMask[0]));
+				} else {
+					mb.setMasked(MatchField.IPV6_ND_TLL, MacAddress.of(dataMask[0]), MacAddress.of(dataMask[1]));
+				}
 				break;
 			case STR_IPV6_ND_TARGET:
 				if (ver10 == true) {
 					throw new IllegalArgumentException("OF Version incompatible");
-					//throw new Exception("OF Version incompatible");
 				}
-				mb.setExact(MatchField.IPV6_ND_TARGET, IPv6Address.of(key_value[1]));
+				mb.setMasked(MatchField.IPV6_ND_TARGET, IPv6AddressWithMask.of(key_value[1]));
 				break;
-				//sanjivini	
-
 			case STR_ARP_OPCODE:
-				if (key_value[1].startsWith("0x")) {
-					mb.setExact(MatchField.ARP_OP, ArpOpcode.of(Integer.parseInt(key_value[1].replaceFirst("0x", ""), 16)));
+				if (dataMask.length == 1) {
+					mb.setExact(MatchField.ARP_OP, dataMask[0].contains("0x") ? ArpOpcode.of(Integer.valueOf(dataMask[0].replaceFirst("0x", ""), 16)) : ArpOpcode.of(Integer.valueOf(dataMask[0])));
 				} else {
-					mb.setExact(MatchField.ARP_OP, ArpOpcode.of(Integer.parseInt(key_value[1])));
+					mb.setMasked(MatchField.ARP_OP, dataMask[0].contains("0x") ? ArpOpcode.of(Integer.valueOf(dataMask[0].replaceFirst("0x", ""), 16)) : ArpOpcode.of(Integer.valueOf(dataMask[0])), 
+							dataMask[1].contains("0x") ? ArpOpcode.of(Integer.valueOf(dataMask[1].replaceFirst("0x", ""), 16)) : ArpOpcode.of(Integer.valueOf(dataMask[1])));
 				}
 				break;
 			case STR_ARP_SHA:
-				mb.setExact(MatchField.ARP_SHA, MacAddress.of(key_value[1]));
+				if (dataMask.length == 1) {
+					mb.setExact(MatchField.ARP_SHA, MacAddress.of(dataMask[0]));
+				} else {
+					mb.setMasked(MatchField.ARP_SHA, MacAddress.of(dataMask[0]), MacAddress.of(dataMask[1]));
+				}
 				break;
 			case STR_ARP_DHA:
-				mb.setExact(MatchField.ARP_THA, MacAddress.of(key_value[1]));
+				if (dataMask.length == 1) {
+					mb.setExact(MatchField.ARP_THA, MacAddress.of(dataMask[0]));
+				} else {
+					mb.setMasked(MatchField.ARP_THA, MacAddress.of(dataMask[0]), MacAddress.of(dataMask[1]));
+				}
 				break;
 			case STR_ARP_SPA:
-				mb.setExact(MatchField.ARP_SPA, IPv4Address.of(key_value[1]));
+				mb.setMasked(MatchField.ARP_SPA, IPv4AddressWithMask.of(key_value[1]));
 				break;
 			case STR_ARP_DPA:
-				mb.setExact(MatchField.ARP_TPA, IPv4Address.of(key_value[1]));
+				mb.setMasked(MatchField.ARP_TPA, IPv4AddressWithMask.of(key_value[1]));
 				break;
 			case STR_MPLS_LABEL:
-				if (key_value[1].startsWith("0x")) {
-					mb.setExact(MatchField.MPLS_LABEL, U32.of(Long.parseLong(key_value[1].replaceFirst("0x", ""), 16)));
+				if (dataMask.length == 1) {
+					mb.setExact(MatchField.MPLS_LABEL, dataMask[0].contains("0x") ? U32.of(Long.valueOf(dataMask[0].replaceFirst("0x", ""), 16)) : U32.of(Long.valueOf(dataMask[0])));
 				} else {
-					mb.setExact(MatchField.MPLS_LABEL, U32.of(Long.parseLong(key_value[1])));
+					mb.setMasked(MatchField.MPLS_LABEL, dataMask[0].contains("0x") ? U32.of(Long.valueOf(dataMask[0].replaceFirst("0x", ""), 16)) : U32.of(Long.valueOf(dataMask[0])), 
+							dataMask[1].contains("0x") ? U32.of(Long.valueOf(dataMask[1].replaceFirst("0x", ""), 16)) : U32.of(Long.valueOf(dataMask[1])));
 				}
 				break;
 			case STR_MPLS_TC:
-				if (key_value[1].startsWith("0x")) {
-					mb.setExact(MatchField.MPLS_TC, U8.of(Short.parseShort(key_value[1].replaceFirst("0x", ""), 16)));
+				if (dataMask.length == 1) {
+					mb.setExact(MatchField.MPLS_TC, dataMask[0].contains("0x") ? U8.of(Short.valueOf(dataMask[0].replaceFirst("0x", ""), 16)) : U8.of(Short.valueOf(dataMask[0])));
 				} else {
-					mb.setExact(MatchField.MPLS_TC, U8.of(Short.parseShort(key_value[1])));
+					mb.setMasked(MatchField.MPLS_TC, dataMask[0].contains("0x") ? U8.of(Short.valueOf(dataMask[0].replaceFirst("0x", ""), 16)) : U8.of(Short.valueOf(dataMask[0])), 
+							dataMask[1].contains("0x") ? U8.of(Short.valueOf(dataMask[1].replaceFirst("0x", ""), 16)) : U8.of(Short.valueOf(dataMask[1])));
 				}
 				break;
 			case STR_MPLS_BOS:
 				mb.setExact(MatchField.MPLS_BOS, key_value[1].equalsIgnoreCase("true") ? OFBooleanValue.TRUE : OFBooleanValue.FALSE);
 				break;
 			case STR_METADATA:
-				if (key_value[1].startsWith("0x")) {
-					mb.setExact(MatchField.METADATA, OFMetadata.ofRaw(Long.parseLong(key_value[1].replaceFirst("0x", ""), 16)));
+				if (dataMask.length == 1) {
+					mb.setExact(MatchField.METADATA, dataMask[0].contains("0x") ? OFMetadata.ofRaw(Long.valueOf(dataMask[0].replaceFirst("0x", ""), 16)) : OFMetadata.ofRaw(Long.valueOf(dataMask[0])));
 				} else {
-					mb.setExact(MatchField.METADATA, OFMetadata.ofRaw(Long.parseLong(key_value[1])));
+					mb.setMasked(MatchField.METADATA, dataMask[0].contains("0x") ? OFMetadata.ofRaw(Long.valueOf(dataMask[0].replaceFirst("0x", ""), 16)) : OFMetadata.ofRaw(Long.valueOf(dataMask[0])), 
+							dataMask[1].contains("0x") ? OFMetadata.ofRaw(Long.valueOf(dataMask[1].replaceFirst("0x", ""), 16)) : OFMetadata.ofRaw(Long.valueOf(dataMask[1])));
 				}
 				break;
 			case STR_TUNNEL_ID:
-				if (key_value[1].startsWith("0x")) {
-					mb.setExact(MatchField.TUNNEL_ID, U64.of(Long.parseLong(key_value[1].replaceFirst("0x", ""), 16)));
+				if (dataMask.length == 1) {
+					mb.setExact(MatchField.TUNNEL_ID, dataMask[0].contains("0x") ? U64.of(Long.valueOf(dataMask[0].replaceFirst("0x", ""), 16)) : U64.of(Long.valueOf(dataMask[0])));
 				} else {
-					mb.setExact(MatchField.TUNNEL_ID, U64.of(Long.parseLong(key_value[1])));
+					mb.setMasked(MatchField.TUNNEL_ID, dataMask[0].contains("0x") ? U64.of(Long.valueOf(dataMask[0].replaceFirst("0x", ""), 16)) : U64.of(Long.valueOf(dataMask[0])), 
+							dataMask[1].contains("0x") ? U64.of(Long.valueOf(dataMask[1].replaceFirst("0x", ""), 16)) : U64.of(Long.valueOf(dataMask[1])));
 				}
 				break;
 			case STR_PBB_ISID:
-- 
GitLab