diff --git a/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java b/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java
index 7f3463bddb7f97410f1252afd51dd8021b16726d..50d471a794c6a03bb20b452f3edf0934cd6711e5 100644
--- a/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java
@@ -46,7 +46,9 @@ import org.projectfloodlight.openflow.protocol.OFHello;
 import org.projectfloodlight.openflow.protocol.OFHelloElem;
 import org.projectfloodlight.openflow.protocol.OFHelloElemVersionbitmap;
 import org.projectfloodlight.openflow.protocol.OFMessage;
+import org.projectfloodlight.openflow.protocol.OFPacketIn;
 import org.projectfloodlight.openflow.protocol.OFPortStatus;
+import org.projectfloodlight.openflow.protocol.OFType;
 import org.projectfloodlight.openflow.protocol.OFVersion;
 import org.projectfloodlight.openflow.protocol.ver13.OFHelloElemTypeSerializerVer13;
 import org.projectfloodlight.openflow.protocol.ver14.OFHelloElemTypeSerializerVer14;
@@ -122,7 +124,7 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
 			// we only expect features reply in the WAIT_FEATURES_REPLY state
 			illegalMessageReceived(m);
 		}
-		
+
 		void processOFPortStatus(OFPortStatus m) {
 			unhandledMessageReceived(m);
 		}
@@ -419,7 +421,7 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
 				super.processOFHello(m); /* Versions don't match as they should; abort */
 			}
 		}
-		
+
 		@Override
 		void processOFPortStatus(OFPortStatus m) {
 			log.warn("Ignoring PORT_STATUS message from {} during OpenFlow channel establishment. Ports will be explicitly queried in a later state.", channel.getRemoteAddress());
@@ -429,6 +431,15 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
 		void enterState() throws IOException {
 			sendFeaturesRequest();
 		}
+
+		@Override
+		void processOFMessage(OFMessage m) throws IOException {
+			if (m.getType().equals(OFType.PACKET_IN)) {
+				log.warn("Ignoring PACKET_IN message from {} during OpenFlow channel establishment.", channel.getRemoteAddress());
+			} else {
+				super.processOFMessage(m);
+			}
+		}
 	};
 
 	/**
@@ -856,8 +867,8 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
 		// Send initial hello message
 		List<OFHelloElem> he = new ArrayList<OFHelloElem>();
 		he.add(factory.buildHelloElemVersionbitmap()
-						.setBitmaps(ofBitmaps)
-						.build());
+				.setBitmaps(ofBitmaps)
+				.build());
 		OFHello.Builder builder = factory.buildHello()
 				.setXid(handshakeTransactionIds--)
 				.setElements(he);
diff --git a/src/main/java/net/floodlightcontroller/util/OFDPAUtils.java b/src/main/java/net/floodlightcontroller/util/OFDPAUtils.java
index 9aca3e4624b9dfd049350b785458f16dfeb84a65..f4276466454a492dd65921625886ca3badd3cc43 100644
--- a/src/main/java/net/floodlightcontroller/util/OFDPAUtils.java
+++ b/src/main/java/net/floodlightcontroller/util/OFDPAUtils.java
@@ -6,15 +6,20 @@ import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 
+import javax.annotation.Nonnull;
+
 import net.floodlightcontroller.core.IOFSwitch;
 
+import org.projectfloodlight.openflow.protocol.OFBucket;
 import org.projectfloodlight.openflow.protocol.OFFlowAdd;
+import org.projectfloodlight.openflow.protocol.OFGroupAdd;
+import org.projectfloodlight.openflow.protocol.OFGroupType;
+import org.projectfloodlight.openflow.protocol.OFVersion;
 import org.projectfloodlight.openflow.protocol.action.OFAction;
 import org.projectfloodlight.openflow.protocol.instruction.OFInstruction;
 import org.projectfloodlight.openflow.protocol.match.Match;
 import org.projectfloodlight.openflow.protocol.match.MatchField;
 import org.projectfloodlight.openflow.protocol.match.MatchFields;
-import org.projectfloodlight.openflow.types.EthType;
 import org.projectfloodlight.openflow.types.OFBufferId;
 import org.projectfloodlight.openflow.types.OFGroup;
 import org.projectfloodlight.openflow.types.OFPort;
@@ -58,10 +63,18 @@ import org.slf4j.LoggerFactory;
 public class OFDPAUtils {
 
 	private OFDPAUtils() {}
-	
+
 	private static final Logger log = LoggerFactory.getLogger(OFDPAUtils.class);
 
-	private static class OFDPAGroupType { // TODO change these enums to static classes so that we don't rely on the ordering...
+	/* These cannot be changed, since they're final (public == okay for the ones we want to inform about) */
+	public static final int PRIORITY = 1000;
+	public static final int DLF_PRIORITY = 0;
+	public static final int HARD_TIMEOUT = 0;
+	public static final int IDLE_TIMEOUT = 0;
+	private static final long APP_COOKIE = 1000;
+	public static final U64 COOKIE = U64.of(APP_COOKIE);
+
+	private static class OFDPAGroupType {
 		static final int L2_INTERFACE = 0;			/* 0 */
 		static final int L2_REWRITE = 1; 			/* 1 */
 		static final int L3_UNICAST = 2;				/* 2 */
@@ -90,7 +103,7 @@ public class OFDPAUtils {
 		static final int MPLS_L3_VPN_LABEL = 2;
 		static final int MPLS_TUNNEL_LABEL_1 = 3;
 		static final int MPLS_TUNNEL_LABEL_2 = 4;
-	    static final int MPLS_SWAP_LABEL = 5;
+		static final int MPLS_SWAP_LABEL = 5;
 		static final int MPLS_FAST_FAILOVER = 6;
 		static final int MPLS_ECMP = 8;
 		static final int MPLS_L2_TAG = 10;
@@ -124,20 +137,19 @@ public class OFDPAUtils {
 							MatchFields.ARP_SPA,
 							MatchFields.ICMPV4_CODE,
 							MatchFields.ICMPV4_TYPE,
-							
+
 							MatchFields.IPV6_SRC,
 							MatchFields.IPV6_DST,
 							MatchFields.IPV6_FLABEL,
 							MatchFields.ICMPV6_CODE,
 							MatchFields.ICMPV6_TYPE,
-							
+
 							MatchFields.TCP_SRC,
 							MatchFields.TCP_DST,
 							MatchFields.UDP_SRC,
 							MatchFields.UDP_DST,
 							MatchFields.SCTP_SRC,
 							MatchFields.SCTP_DST
-							// TODO fill in rest of ARP and IPV6 stuff here 
 							)
 					);
 
@@ -171,7 +183,7 @@ public class OFDPAUtils {
 	public static List<MatchFields> getSupportedMatchFields() {
 		return ALLOWED_MATCHES;
 	}
-	
+
 	/**
 	 * Examine all the MatchFields in a Match object and pick out
 	 * the MatchFields that are not supported by OF-DPA.
@@ -182,7 +194,7 @@ public class OFDPAUtils {
 	public static List<MatchFields> checkMatchFields(Match m) {
 		List<MatchFields> unsupported = null;
 		Iterator<MatchField<?>> mfi = m.getMatchFields().iterator();
-		
+
 		while (mfi.hasNext()) {
 			MatchField<?> mf = mfi.next();
 			if (!getSupportedMatchFields().contains(mf.id)) {
@@ -192,11 +204,233 @@ public class OFDPAUtils {
 				unsupported.add(mf.id);
 			}
 		}
-		
+
 		return unsupported;
 	}
 
 	/**
+	 * Add the OFDPA groups and flows necessary to facilitate future forwarding/learning 
+	 * switch flows. The switch provided must be an OFDPA 2.0 compliant switch.
+	 * Use VLAN tag of null or VlanVid.ZERO for untagged. VLAN tag 1 may not be used; it is 
+	 * reserved as an internal VLAN.
+	 * 
+	 * This function will add the flows that permit all packets in the VLAN specified and
+	 * on the ports specified to reach the policy ACL table. The policy ACL table (60) of
+	 * the OF-DPA switch will contain a DLF, zero-priority flow to forward all packets to
+	 * the controller for processing. A packet forwarded to the controller from the policy
+	 * ACL table can be either handled manually or have a flow inserted for it via 
+	 * {@link OFDPAUtils#addBridgingFlow(IOFSwitch, U64, int, int, int, Match, VlanVid, OFPort) this function}.
+	 * 
+	 * @param sw
+	 * @param vlan
+	 * @param ports
+	 * @return
+	 */
+	public static boolean addLearningSwitchPrereqs(@Nonnull IOFSwitch sw, VlanVid vlan, @Nonnull List<OFPortModeTuple> ports) {
+		/*
+		 * Both of these must complete. If the first fails, the second will not be executed. (AND short-circuit)
+		 */
+		return addLearningSwitchPrereqGroups(sw, vlan, ports) && addLearningSwitchPrereqFlows(sw, vlan, ports);
+	}
+
+	/**
+	 * Note: Prefer {@link OFDPAUtils#addLearningSwitchPrereqs(IOFSwitch, VlanVid, List) this function}
+	 * over this function unless you know what you are doing.
+	 * 
+	 * Add the OFDPA groups necessary to facilitate future forwarding/learning 
+	 * switch flows. The switch provided must be an OFDPA 2.0 compliant switch.
+	 * If a VLAN tag is provided, it will be expected that all ports in the 
+	 * accompanying list of ports are tagged and not access. Use VLAN tag of 
+	 * null or VlanVid.ZERO for untagged. VLAN tag 1 may not be used; it is 
+	 * reserved as an internal VLAN.
+	 * 
+	 * @param sw
+	 * @param vlan
+	 * @param ports
+	 * @return
+	 */
+	public static boolean addLearningSwitchPrereqGroups(@Nonnull IOFSwitch sw, VlanVid vlan, @Nonnull List<OFPortModeTuple> ports) {
+		if (sw == null) {
+			throw new NullPointerException("Switch cannot be null.");
+		}
+		if (vlan == null) {
+			vlan = VlanVid.ZERO; /* set untagged */
+		} else if (vlan.equals(VlanVid.ofVlan(1))) {
+			throw new IllegalArgumentException("VLAN cannot be 1. VLAN 1 is an reserved VLAN for internal use inside the OFDPA switch.");
+		}
+		if (ports == null) {
+			throw new NullPointerException("List of ports cannot be null. Must specify at least 2 valid switch ports.");
+		} else if (ports.size() < 2) {
+			throw new IllegalArgumentException("List of ports must contain at least 2 valid switch ports.");
+		} else {
+			/* verify ports are valid switch ports */
+			for (OFPortModeTuple p : ports) {
+				if (sw.getOFFactory().getVersion().equals(OFVersion.OF_10) && (sw.getPort(p.getPort()) == null || p.getPort().getShortPortNumber() > 0xFF00)) {
+					throw new IllegalArgumentException("Port " + p.getPort().getPortNumber() + " is not a valid port on switch " + sw.getId().toString());
+				} else if (!sw.getOFFactory().getVersion().equals(OFVersion.OF_10) && (sw.getPort(p.getPort()) == null || p.getPort().getPortNumber() > 0xffFFff00)) {
+					throw new IllegalArgumentException("Port " + p.getPort().getPortNumber() + " is not a valid port on switch " + sw.getId().toString());
+				}
+			}
+		}
+
+		/*
+		 * For each output port, add an L2 interface group.
+		 */		
+		for (OFPortModeTuple p : ports) {
+			List<OFAction> actions = new ArrayList<OFAction>();
+			if (vlan.equals(VlanVid.ZERO) || p.getMode() == OFPortMode.ACCESS) { /* if it's untagged (internal=1) or access, pop the tag */
+				actions.add(sw.getOFFactory().actions().popVlan());
+			}
+			actions.add(sw.getOFFactory().actions().output(p.getPort(), 0xffFFffFF));
+
+			OFGroupAdd ga = sw.getOFFactory().buildGroupAdd()
+					.setGroup(GroupIds.createL2Interface(p.getPort(), (vlan.equals(VlanVid.ZERO) ? VlanVid.ofVlan(1) : vlan)))
+					.setGroupType(OFGroupType.INDIRECT)
+					.setBuckets(Collections.singletonList(
+							sw.getOFFactory().buildBucket()
+							.setActions(actions)
+							.build()))
+							.build();
+			sw.write(ga);
+		}
+
+		/*
+		 * For the VLAN provided (or internal VLAN 1 if untagged),
+		 * add an L2 flood group.
+		 */
+		List<OFBucket> bucketList = new ArrayList<OFBucket>(ports.size());
+		for (OFPortModeTuple p : ports) {
+			List<OFAction> actions = new ArrayList<OFAction>();
+			if (vlan.equals(VlanVid.ZERO) || p.getMode() == OFPortMode.ACCESS) { /* ditto */
+				actions.add(sw.getOFFactory().actions().popVlan());
+			}
+			actions.add(sw.getOFFactory().actions().output(p.getPort(), 0xffFFffFF));
+			bucketList.add(sw.getOFFactory().buildBucket().setActions(actions).build());
+		}
+		OFGroupAdd ga = sw.getOFFactory().buildGroupAdd() /* use the VLAN ID as the group ID */
+				.setGroup(GroupIds.createL2Flood(U16.of(vlan.getVlan()), vlan.equals(VlanVid.ZERO) ? VlanVid.ofVlan(1) : vlan))
+				.setGroupType(OFGroupType.INDIRECT)
+				.setBuckets(bucketList)
+				.build();
+		sw.write(ga);
+
+		return true;
+	}
+
+	/**
+	 * Note: Prefer {@link OFDPAUtils#addLearningSwitchPrereqs(IOFSwitch, VlanVid, List) this function}
+	 * over this function unless you know what you are doing.
+	 * 
+	 * Add the OFDPA flows necessary to facilitate future forwarding/learning 
+	 * switch flows. The switch provided must be an OFDPA 2.0 compliant switch.
+	 * If a VLAN tag is provided, it will be expected that all ports in the 
+	 * accompanying list of ports are tagged and not access. Use VLAN tag of 
+	 * null or VlanVid.ZERO for untagged. VLAN tag 1 may not be used; it is 
+	 * reserved as an internal VLAN.
+	 * 
+	 * @param sw
+	 * @param vlan
+	 * @param ports
+	 * @return
+	 */
+	public static boolean addLearningSwitchPrereqFlows(@Nonnull IOFSwitch sw, VlanVid vlan, @Nonnull List<OFPortModeTuple> ports) {
+		if (sw == null) {
+			throw new NullPointerException("Switch cannot be null.");
+		}
+		if (vlan == null) {
+			vlan = VlanVid.ZERO; /* set untagged */
+		} else if (vlan.equals(VlanVid.ofVlan(1))) {
+			throw new IllegalArgumentException("VLAN cannot be 1. VLAN 1 is an reserved VLAN for internal use inside the OFDPA switch.");
+		}
+		if (ports == null) {
+			throw new NullPointerException("List of ports cannot be null. Must specify at least 2 valid switch ports.");
+		} else if (ports.size() < 2) {
+			throw new IllegalArgumentException("List of ports must contain at least 2 valid switch ports.");
+		} else {
+			/* verify ports are valid switch ports */
+			for (OFPortModeTuple p : ports) {
+				if (sw.getOFFactory().getVersion().equals(OFVersion.OF_10) && (sw.getPort(p.getPort()) == null || p.getPort().getShortPortNumber() > 0xFF00)) {
+					throw new IllegalArgumentException("Port " + p.getPort().getPortNumber() + " is not a valid port on switch " + sw.getId().toString());
+				} else if (!sw.getOFFactory().getVersion().equals(OFVersion.OF_10) && (sw.getPort(p.getPort()) == null || p.getPort().getPortNumber() > 0xffFFff00)) {
+					throw new IllegalArgumentException("Port " + p.getPort().getPortNumber() + " is not a valid port on switch " + sw.getId().toString());
+				}
+			}
+		}
+
+		/*
+		 * VLAN flow table (10) will drop by default, so we need to
+		 * add a flow that will always direct the packet to the next
+		 * flow table, the termination MAC table (20). If a VLAN tag
+		 * is not present, then we need to append a tag. The tag to
+		 * append will either be the tag specified in outVlan or the
+		 * default tag of 1. Only VLANs are handled in this table.
+		 */
+		ArrayList<OFInstruction> instructions = new ArrayList<OFInstruction>();
+		ArrayList<OFAction> actions = new ArrayList<OFAction>();
+		Match.Builder mb = sw.getOFFactory().buildMatch();
+		OFFlowAdd.Builder fab = sw.getOFFactory().buildFlowAdd();
+		
+		/* These are common to all flows for VLAN flow table. */
+		fab.setBufferId(OFBufferId.NO_BUFFER)
+			.setCookie(COOKIE)
+			.setHardTimeout(HARD_TIMEOUT)
+			.setIdleTimeout(IDLE_TIMEOUT)
+			.setPriority(PRIORITY)
+			.setTableId(Tables.VLAN);
+
+		for (OFPortModeTuple p : ports) {
+			/* If VLAN tag not present add a tag (internal=1 or defined) */
+			if (vlan.equals(VlanVid.ZERO) || p.getMode() == OFPortMode.ACCESS) { /* push tag if access or untagged entirely */
+				mb.setExact(MatchField.VLAN_VID, OFVlanVidMatch.UNTAGGED);
+				// happens automatically actions.add(sw.getOFFactory().actions().buildPushVlan().setEthertype(EthType.VLAN_FRAME).build());
+				actions.add(sw.getOFFactory().actions().setField(sw.getOFFactory().oxms().vlanVid(OFVlanVidMatch.ofVlanVid((vlan.equals(VlanVid.ZERO) ? VlanVid.ofVlan(1) : vlan)))));
+				instructions.add(sw.getOFFactory().instructions().applyActions(actions));
+			} else {
+				mb.setExact(MatchField.VLAN_VID, OFVlanVidMatch.ofVlanVid((vlan.equals(VlanVid.ZERO) ? VlanVid.ofVlan(1) : vlan)));
+			}
+
+			/* No matter what, we need to match on the ingress port */
+			mb.setExact(MatchField.IN_PORT, p.getPort());
+
+			/* No matter what, output to the next table */
+			instructions.add(sw.getOFFactory().instructions().gotoTable(Tables.TERMINATION_MAC));
+
+			/* Set the new stuff. */
+			fab.setInstructions(instructions)
+				.setMatch(mb.build())
+				.build();
+			sw.write(fab.build());
+			log.debug("Writing prereq flow to VLAN flow table {}", fab.build().toString());
+
+			/* Don't forget to empty out our containers for the next iteration (or below). */
+			instructions.clear();
+			actions.clear();
+			mb = sw.getOFFactory().buildMatch();
+		}
+
+		/*
+		 * We will insert a DLF flow to send to controller in the Policy ACL table (60).
+		 * TODO Maybe this isn't the best choice, since we assume bypass of bridging and unicast/mulicast routing tables.
+		 */
+		actions.add(sw.getOFFactory().actions().output(OFPort.CONTROLLER, 0xffFFffFF));
+		instructions.add(sw.getOFFactory().instructions().applyActions(actions));
+		fab = fab.setMatch(sw.getOFFactory().buildMatch().build()) /* clear match */
+				.setInstructions(instructions)
+				.setPriority(DLF_PRIORITY) /* different zero priority and table ID here */
+				.setTableId(Tables.POLICY_ACL);
+		sw.write(fab.build());
+		log.debug("Writing DLF flow to policy ACL table {}", fab.build().toString());
+
+		return true;
+	}
+
+	/**
+	 * Note: Must have individually added {@link OFDPAUtils#addLearningSwitchPrereqGroups(IOFSwitch, VlanVid, List) groups} 
+	 * and then {@link OFDPAUtils#addLearningSwitchPrereqFlows(IOFSwitch, VlanVid, List) flows},
+	 * or must have done {@link OFDPAUtils#addLearningSwitchPrereqs(IOFSwitch, VlanVid, List) both} prior to calling 
+	 * this function. It is assumed you have done the aforementioned with the same VLAN and ports, otherwise you will likely
+	 * get a very grumpy OF-DPA switch.
+	 * 
 	 * Based on an intent described by a Match and an output OFPort,
 	 * add a flow to an OF-DPA switch, conforming to the pipeline it
 	 * exposes. The provided Match must contain at least the destination
@@ -246,7 +480,7 @@ public class OFDPAUtils {
 				return false;
 			}
 		}
-		outVlan = (outVlan == null ? VlanVid.ofVlan(0) : outVlan);
+		outVlan = (outVlan == null ? VlanVid.ZERO : outVlan);
 		outPort = (outPort == null ? OFPort.ZERO : outPort);
 
 		/*
@@ -255,83 +489,38 @@ public class OFDPAUtils {
 		 */
 
 		/*
-		 * VLAN flow table (10) will drop by default, so we need to
-		 * add a flow that will always direct the packet to the next
-		 * flow table, the termination MAC table (20). If a VLAN tag
-		 * is not present, then we need to append a tag. The tag to
-		 * append will either be the tag specified in outVlan or the
-		 * default tag of 1. Only VLANs are handled in this table.
+		 * VLAN flow table (10) is handled by prereq flows.
 		 */
-		ArrayList<OFInstruction> instructions = new ArrayList<OFInstruction>();
-		ArrayList<OFAction> actions = new ArrayList<OFAction>();
-		Match.Builder mb = sw.getOFFactory().buildMatch();
-
-		/* If VLAN tag not present, add the internal tag of 1 */
-		if (!match.isExact(MatchField.VLAN_VID)) {
-			mb.setExact(MatchField.VLAN_VID, OFVlanVidMatch.UNTAGGED);
-			actions.add(sw.getOFFactory().actions().buildPushVlan().setEthertype(EthType.VLAN_FRAME).build());
-			actions.add(sw.getOFFactory().actions().setField(sw.getOFFactory().oxms().vlanVid(OFVlanVidMatch.ofVlan(1))));
-			instructions.add(sw.getOFFactory().instructions().applyActions(actions));
-		} else {
-			mb.setExact(MatchField.VLAN_VID, match.get(MatchField.VLAN_VID));
-		}
-
-		/* No matter what, output to the next table */
-		instructions.add(sw.getOFFactory().instructions().gotoTable(Tables.TERMINATION_MAC));
-
-		OFFlowAdd fa = sw.getOFFactory().buildFlowAdd()
-				.setBufferId(OFBufferId.NO_BUFFER)
-				.setCookie(cookie)
-				.setHardTimeout(hardTimeout)
-				.setIdleTimeout(idleTimeout)
-				.setPriority(priority)
-				.setInstructions(instructions)
-				.setTableId(Tables.VLAN)
-				.setMatch(mb.build())
-				.build();
-		sw.write(fa);
 
 		/*
 		 * Termination MAC table (20) will automatically send to the
 		 * bridging flow table (50), so also insert nothing here.
+		 * 
+		 * Can send to controller.
 		 */
 
 		/*
 		 * Unicast routing (30) and multicast routing (40) flow tables
 		 * are special use-case tables the application should program 
 		 * directly. As such, we won't consider them here.
+		 * 
+		 * Can send to controller.
 		 */
 
 		/*
 		 * Bridging table (50) should assign a write-action goto-group 
 		 * depending on the desired output action (single-port or 
-		 * flood). This can also be done in the next table, policy ACL,
+		 * flood). But, the default on miss is to go to the policy ACL
+		 * table (60), which we will do. Policy ACL can also assign the group.
 		 * which we will do. Bridging must match on the VLAN VID and the
 		 * dest MAC address of the packet. It must have a priority greater
 		 * than all less-specific flows in the table (i.e. wildcarded
 		 * flows). We will reserve priority 0 for a DLF (destination
 		 * lookup failure) flow, which would have all fields wildcarded.
-		 * The default on miss is to send to the policy ACL table (60),
-		 * but this will not occur if our flow is matched, so we need to
-		 * do this specifically in each flow we insert.
+		 * 
+		 * Can send to controller.
 		 */
 
-		instructions.clear();
-		actions.clear();
-		
-		/* No matter what, output to the next table */
-		instructions.add(sw.getOFFactory().instructions().gotoTable(Tables.POLICY_ACL));
-
-		fa = fa.createBuilder()
-				.setTableId(Tables.BRIDGING)
-				.setMatch(sw.getOFFactory().buildMatch()
-						.setExact(MatchField.VLAN_VID, fa.getMatch().get(MatchField.VLAN_VID)) /* might have been set to 1, so grab it here */
-						.setExact(MatchField.ETH_DST, match.get(MatchField.ETH_DST))
-						.build())
-						.setInstructions(instructions)
-						.build();
-		sw.write(fa);
-
 		/*
 		 * Policy ACL table (60) allows for more detailed matches. This
 		 * is where we will implement all the matches specified in the Match
@@ -341,19 +530,22 @@ public class OFDPAUtils {
 		 * drop a packet for not matching, then no output group will be
 		 * assigned to the packet, thus dropping it.
 		 * 
-		 * A DLF (desintation lookup failure) flow can also be inserted
+		 * A DLF (destination lookup failure) flow can also be inserted
 		 * here to forward packets to the controller.
+		 * 
+		 * Can send to controller.
 		 */
-		
+
+		ArrayList<OFInstruction> instructions = new ArrayList<OFInstruction>();
+		ArrayList<OFAction> actions = new ArrayList<OFAction>();
+
 		/* Set the group to which we want to output. This might be a L2 flood or interface. */
-		instructions.clear();
-		actions.clear();
 		if (outPort.equals(OFPort.ZERO)) {
 			/* Don't add a group at all --> DROP */
 		} else if (outPort.equals(OFPort.FLOOD) || outPort.equals(OFPort.ALL)) { // TODO how to distinguish in OF-DPA?
 			actions.add(
 					sw.getOFFactory().actions().group( // TODO Assume there is only one flood group per VLAN
-							GroupIds.createL2Flood(U16.ZERO, fa.getMatch().get(MatchField.VLAN_VID).getVlanVid())
+							GroupIds.createL2Flood(U16.ZERO, (outVlan.equals(VlanVid.ZERO) ? VlanVid.ofVlan(1) : outVlan))
 							)
 					);
 			instructions.add(sw.getOFFactory().instructions().writeActions(actions));
@@ -363,14 +555,19 @@ public class OFDPAUtils {
 		} else { /* assume port is a number valid on the switch */
 			actions.add(
 					sw.getOFFactory().actions().group(
-							GroupIds.createL2Interface(outPort, fa.getMatch().get(MatchField.VLAN_VID).getVlanVid())
+							GroupIds.createL2Interface(outPort, (outVlan.equals(VlanVid.ZERO) ? VlanVid.ofVlan(1) : outVlan))
 							)
 					);
 			instructions.add(sw.getOFFactory().instructions().writeActions(actions));
 		}
-		
-		/* We're allowed to match on anything in the Match at this point. */
-		fa = fa.createBuilder()
+
+		/* We're allowed to match on anything in the Match (supplied as an argument to this function) at this point. */
+		OFFlowAdd fa = sw.getOFFactory().buildFlowAdd()
+				.setBufferId(OFBufferId.NO_BUFFER)
+				.setCookie(cookie)
+				.setHardTimeout(hardTimeout)
+				.setIdleTimeout(idleTimeout)
+				.setPriority(priority)
 				.setTableId(Tables.POLICY_ACL)
 				.setMatch(match)
 				.setInstructions(instructions)
@@ -508,7 +705,7 @@ public class OFDPAUtils {
 		 */
 		public static OFGroup createMPLSL3VPNLabel(U32 index) { //9
 			return OFGroup.of(0 | (index.getRaw() & 0x00ffFFff) 
-					| (MPLSSubType.MPLS_INTERFACE << 24)
+					| (MPLSSubType.MPLS_L3_VPN_LABEL << 24)
 					| (OFDPAGroupType.MPLS_LABEL << 28));
 		}
 		/**
@@ -578,4 +775,4 @@ public class OFDPAUtils {
 			return OFGroup.of(0 | p.getShortPortNumber() | (OFDPAGroupType.L2_LOOPBACK << 28));
 		}
 	}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/util/OFPortMode.java b/src/main/java/net/floodlightcontroller/util/OFPortMode.java
new file mode 100644
index 0000000000000000000000000000000000000000..9a02a47fb93a3f54c5960ae5af93490d2a85d2bc
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/util/OFPortMode.java
@@ -0,0 +1,14 @@
+package net.floodlightcontroller.util;
+
+/**
+ * Define the different operating modes of a switch
+ * port as trunk, access, and both (trunk and access).
+ * 
+ * @author Ryan Izard, ryan.izard@bigswitch.com, rizard@g.clemson.edu
+ */
+
+public enum OFPortMode {
+	TRUNK,
+	ACCESS,
+	TRUNK_AND_ACCESS
+}
diff --git a/src/main/java/net/floodlightcontroller/util/OFPortModeTuple.java b/src/main/java/net/floodlightcontroller/util/OFPortModeTuple.java
new file mode 100644
index 0000000000000000000000000000000000000000..0b3fd9cb22f9a77e9a1664b561efeffab4c53c58
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/util/OFPortModeTuple.java
@@ -0,0 +1,72 @@
+package net.floodlightcontroller.util;
+
+import javax.annotation.Nonnull;
+
+import org.projectfloodlight.openflow.types.OFPort;
+
+/**
+ * Make it easier to track a port and it's operating {@link OFPortMode mode}.
+ * Objects of this class are immutable.
+ * 
+ * @author Ryan Izard, ryan.izard@bigswitch.com, rizard@g.clemson.edu
+ */
+public class OFPortModeTuple {
+	private final OFPort p;
+	private final OFPortMode m;
+	
+	private OFPortModeTuple(@Nonnull OFPort p, @Nonnull OFPortMode m) {
+		this.p = p;
+		this.m = m;
+	}
+
+	public static OFPortModeTuple of(OFPort p, OFPortMode m) {
+		if (p == null) {
+			throw new NullPointerException("Port cannot be null.");
+		}
+		if (m == null) {
+			throw new NullPointerException("Mode cannot be null.");
+		}
+		return new OFPortModeTuple(p, m);
+	}
+	
+	public OFPort getPort() {
+		return this.p;
+	}
+	
+	public OFPortMode getMode() {
+		return this.m;
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + ((m == null) ? 0 : m.hashCode());
+		result = prime * result + ((p == null) ? 0 : p.hashCode());
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		OFPortModeTuple other = (OFPortModeTuple) obj;
+		if (m != other.m)
+			return false;
+		if (p == null) {
+			if (other.p != null)
+				return false;
+		} else if (!p.equals(other.p))
+			return false;
+		return true;
+	}
+
+	@Override
+	public String toString() {
+		return "OFPortModeTuple [p=" + p + ", m=" + m + "]";
+	}
+}