diff --git a/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java b/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java
index e341d6dc91133edb8845ccbc24751821a4c26dcf..3732d38e379d2ffc05ee35b6e212ceb28e5843c3 100644
--- a/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java
+++ b/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java
@@ -47,7 +47,7 @@ import net.floodlightcontroller.routing.IRoutingDecision;
 import net.floodlightcontroller.routing.IRoutingService;
 import net.floodlightcontroller.routing.Route;
 import net.floodlightcontroller.topology.ITopologyService;
-import net.floodlightcontroller.util.MatchMaskUtils;
+import net.floodlightcontroller.util.MatchUtils;
 
 import org.projectfloodlight.openflow.protocol.OFFlowMod;
 import org.projectfloodlight.openflow.protocol.match.Match;
@@ -278,11 +278,12 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule {
 												MacAddress srcMac = eth.getSourceMACAddress();
 												MacAddress dstMac = eth.getDestinationMACAddress();
 												
-												Match.Builder mb = m.createBuilder(); // TODO @Ryan based on packet in's match, m; ingress port should be included already, but it's not....
-												mb.setExact(MatchField.IN_PORT, m.get(MatchField.IN_PORT))
-												.setExact(MatchField.ETH_SRC, srcMac)
-												.setExact(MatchField.ETH_DST, dstMac);
-												//.setExact(MatchField.VLAN_VID, OFVlanVidMatch.ofVlanVid(vlan));
+												// A retentive builder will remember all MatchFields of the parent the builder was generated from
+												// With a normal builder, all parent MatchFields will be lost if any MatchFields are added, mod, del
+												Match.Builder mb = MatchUtils.createRetentiveBuilder(m);
+												mb.setExact(MatchField.ETH_SRC, srcMac)
+												.setExact(MatchField.ETH_DST, dstMac)
+												.setExact(MatchField.VLAN_VID, OFVlanVidMatch.ofVlanVid(vlan));
 												
 												if (eth.getEtherType() == Ethernet.TYPE_IPv4) {
 													IPv4 ip = (IPv4) eth.getPayload();
@@ -295,11 +296,7 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule {
 													mb.setExact(MatchField.ETH_TYPE, EthType.ARP);
 												} //TODO @Ryan should probably include other ethertypes
 												
-												// A Match will contain only what you want to match on.
-												// Absence of a MatchField --> it can be anything (wildcarded).
-												// Remove all matches except for L2 and L3 addresses & VLAN
-												// to allow forwarding on a (V)LAN
-												routeMatch = /*MatchMaskUtils.maskL4AndUp(*/mb.build()/*)*/;
+												routeMatch = mb.build();
 											}
 
 											pushRoute(route, routeMatch, pi, sw.getId(), cookie,
diff --git a/src/main/java/net/floodlightcontroller/packet/Ethernet.java b/src/main/java/net/floodlightcontroller/packet/Ethernet.java
index e6fa316db6f2c9e286bad31354351ba20eda4211..2b9d0ea3a88fe2933f63ac2a898471022109635b 100644
--- a/src/main/java/net/floodlightcontroller/packet/Ethernet.java
+++ b/src/main/java/net/floodlightcontroller/packet/Ethernet.java
@@ -23,6 +23,7 @@ import java.util.HashMap;
 import java.util.Map;
 
 import org.projectfloodlight.openflow.types.MacAddress;
+import org.projectfloodlight.openflow.types.VlanVid;
 
 /**
  *
@@ -35,7 +36,7 @@ public class Ethernet extends BasePacket {
     public static final short TYPE_IPv4 = 0x0800;
     public static final short TYPE_LLDP = (short) 0x88cc;
     public static final short TYPE_BSN = (short) 0x8942;
-    public static final short VLAN_UNTAGGED = (short)0xffff;
+    public static final short VLAN_UNTAGGED = VlanVid.ZERO.getVlan(); // untagged vlan must be 0x0000 for loxi. We can use the convenient ZERO field
     public static final short DATALAYER_ADDRESS_LENGTH = 6; // bytes
     public static Map<Short, Class<? extends IPacket>> etherTypeClassMap;
 
diff --git a/src/main/java/net/floodlightcontroller/util/MatchMaskUtils.java b/src/main/java/net/floodlightcontroller/util/MatchMaskUtils.java
deleted file mode 100644
index 67b0a6576d53dca2156efecbec4ff2af1be6db5d..0000000000000000000000000000000000000000
--- a/src/main/java/net/floodlightcontroller/util/MatchMaskUtils.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package net.floodlightcontroller.util;
-
-import org.projectfloodlight.openflow.protocol.OFFactories;
-import org.projectfloodlight.openflow.protocol.match.Match;
-import org.projectfloodlight.openflow.protocol.match.MatchField;
-
-/**
- * Apply certain, routine masks to existing matches.
- *
- * @author Ryan Izard <ryan.izard@bigswitch.com, rizard@g.clemson.edu>
- */
-public class MatchMaskUtils {
-	/**
-	 * Create a point-to-point match for two devices at the IP layer.
-	 * Takes an existing match (e.g. from a PACKET_IN), and masks all
-	 * MatchFields leaving behind:
-	 * 		IN_PORT
-	 * 		VLAN_VID
-	 * 		ETH_SRC
-	 * 		ETH_DST
-	 * 		IPV4_SRC
-	 * 		IPV4_DST
-	 * 
-	 * @param m The match to remove all L4+ MatchFields from
-	 * @return A new Match object with all MatchFields masked/wildcared
-	 * except for those listed above.
-	 */
-	public static Match maskL4AndUp(Match m) {
-		// cannot create builder from existing match; will retain all MatchFields set
-		Match.Builder mb = OFFactories.getFactory(m.getVersion()).buildMatch(); 
-		mb.setExact(MatchField.IN_PORT, m.get(MatchField.IN_PORT))
-		.setExact(MatchField.VLAN_VID, m.get(MatchField.VLAN_VID))
-		.setExact(MatchField.ETH_SRC, m.get(MatchField.ETH_SRC))
-		.setExact(MatchField.ETH_DST, m.get(MatchField.ETH_DST));
-		if (m.get(MatchField.IPV4_SRC) != null) {
-			mb.setExact(MatchField.IPV4_SRC, m.get(MatchField.IPV4_SRC));
-		}
-		if (m.get(MatchField.IPV4_DST) != null) {
-			mb.setExact(MatchField.IPV4_DST, m.get(MatchField.IPV4_DST));
-		}
-		return mb.build();
-	}
-	
-	
-}
diff --git a/src/main/java/net/floodlightcontroller/util/MatchUtils.java b/src/main/java/net/floodlightcontroller/util/MatchUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..1d08b05d4174dde3002761801d74a331eebaa8d5
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/util/MatchUtils.java
@@ -0,0 +1,125 @@
+package net.floodlightcontroller.util;
+
+import java.util.Iterator;
+
+import org.projectfloodlight.openflow.protocol.OFFactories;
+import org.projectfloodlight.openflow.protocol.match.Match;
+import org.projectfloodlight.openflow.protocol.match.MatchField;
+
+/**
+ * Match helper functions. Use with any OpenFlowJ-Loxi Match.
+ *
+ * @author Ryan Izard <ryan.izard@bigswitch.com, rizard@g.clemson.edu>
+ */
+public class MatchUtils {
+	/**
+	 * Create a point-to-point match for two devices at the IP layer.
+	 * Takes an existing match (e.g. from a PACKET_IN), and masks all
+	 * MatchFields leaving behind:
+	 * 		IN_PORT
+	 * 		VLAN_VID
+	 * 		ETH_TYPE
+	 * 		ETH_SRC
+	 * 		ETH_DST
+	 * 		IPV4_SRC
+	 * 		IPV4_DST
+	 * 		IP_PROTO (might remove this)
+	 * 
+	 * If one of the above MatchFields is wildcarded in Match m,
+	 * that MatchField will be wildcarded in the returned Match.
+	 * 
+	 * @param m The match to remove all L4+ MatchFields from
+	 * @return A new Match object with all MatchFields masked/wildcared
+	 * except for those listed above.
+	 */
+	@SuppressWarnings({ "rawtypes", "unchecked" })
+	public static Match maskL4AndUp(Match m) {
+		Match.Builder mb = m.createBuilder(); 
+		Iterator<MatchField<?>> itr = m.getMatchFields().iterator(); // only get exact or masked fields (not fully wildcarded)
+		while(itr.hasNext()) {
+			MatchField mf = itr.next();
+			// restrict MatchFields only to L3 and below: IN_PORT, ETH_TYPE, ETH_SRC, ETH_DST, IPV4_SRC, IPV4_DST, IP_PROTO (this one debatable...)
+			// if a MatchField is not in the access list below, it will not be set --> it will be left wildcarded (default)
+			if (mf.equals(MatchField.IN_PORT) || mf.equals(MatchField.ETH_TYPE) || mf.equals(MatchField.ETH_SRC) || mf.equals(MatchField.ETH_DST) ||
+					mf.equals(MatchField.IPV4_SRC) || mf.equals(MatchField.IPV4_DST) || mf.equals(MatchField.IP_PROTO)) {
+				if (m.isExact(mf)) {
+					mb.setExact(mf, m.get(mf));
+				} else if (m.isPartiallyMasked(mf)) {
+					mb.setMasked(mf, m.getMasked(mf));
+				} else {
+					// it's either exact, masked, or wildcarded
+					// itr only contains exact and masked MatchFields
+					// we should never get here
+				}
+			}
+		}
+		return mb.build();
+	}
+
+	/**
+	 * Create a builder from an existing Match object. Unlike Match's
+	 * createBuilder(), this utility function will preserve all of
+	 * Match m's MatchFields, even if new MatchFields are set or modified
+	 * with the builder after it is returned to the calling function.
+	 * 
+	 * All original MatchFields in m will be set if the build() method is 
+	 * invoked upon the returned builder. After the builder is returned, if
+	 * a MatchField is modified via setExact(), setMasked(), or wildcard(),
+	 * the newly modified MatchField will replace the original found in m.
+	 * 
+	 * @param m; the match to create the builder from
+	 * @return Match.Builder; the builder that can be modified, and when built,
+	 * will retain all of m's MatchFields, unless you explicitly overwrite them.
+	 */
+	@SuppressWarnings({ "unchecked", "rawtypes" })
+	public static Match.Builder createRetentiveBuilder(Match m) {
+		/* Builder retains a parent MatchField list, but list will not be used to  
+		 * build the new match if the builder's set methods have been invoked; only 
+		 * additions will be built, and all parent MatchFields will be ignored,  
+		 * even if they were not modified by the new builder. Create a builder, and
+		 * walk through m's list of non-wildcarded MatchFields. Set them all in the
+		 * new builder by invoking a set method for each. This will make them persist
+		 * in the Match built from this builder if the user decides to add or subtract
+		 * from the MatchField list.
+		 */
+		Match.Builder mb = m.createBuilder(); 
+		Iterator<MatchField<?>> itr = m.getMatchFields().iterator(); // only get exact or masked fields (not fully wildcarded)
+		while(itr.hasNext()) {
+			MatchField mf = itr.next();
+			if (m.isExact(mf)) {
+				mb.setExact(mf, m.get(mf));
+			} else if (m.isPartiallyMasked(mf)) {
+				mb.setMasked(mf, m.getMasked(mf));
+			} else {
+				// it's either exact, masked, or wildcarded
+				// itr only contains exact and masked MatchFields
+				// we should never get here
+			}
+		}
+		return mb;
+	}
+	
+	/**
+	 * Create a Match builder the same OF version as Match m. The returned builder
+	 * will not retain any MatchField information from Match m and will
+	 * essentially return a clean-slate Match builder with no parent history. 
+	 * This simple method is included as a wrapper to provide the opposite functionality
+	 * of createRetentiveBuilder().
+	 * 
+	 * @param m; the match to create the builder from
+	 * @return Match.Builder; the builder retains no history from the parent Match m
+	 */
+	public static Match.Builder createForgetfulBuilder(Match m) {
+		return OFFactories.getFactory(m.getVersion()).buildMatch();
+	}
+	
+	/**
+	 * Create a duplicate Match object from Match m.
+	 * 
+	 * @param m; the match to copy
+	 * @return Match; the new copy of Match m
+	 */
+	public static Match createCopy(Match m) {
+		return m.createBuilder().build(); // will use parent MatchFields to produce the new Match only if the builder is never modified
+	}
+}