diff --git a/build.xml b/build.xml
index 2b4989e597f2b98868c8c16d533547f72b194a92..a430867363dfacede2ee55be60f7af9389a72113 100644
--- a/build.xml
+++ b/build.xml
@@ -53,13 +53,14 @@
     <patternset id="lib">
         <include name="logback-classic-1.0.0.jar"/>
         <include name="logback-core-1.0.0.jar"/>
-        <include name="jackson-core-2.1.4.jar"/>
-        <include name="jackson-annotations-2.1.4.jar"/>
-        <include name="jackson-databind-2.1.4.jar"/>
-        <include name="jackson-dataformat-smile-2.1.4.jar"/>
-        <include name="jackson-dataformat-xml-2.1.4.jar"/>
-        <include name="jackson-dataformat-yaml-2.1.4.jar"/>
-        <include name="jackson-dataformat-csv-2.1.4.jar"/>
+        <include name="jackson-core-2.4.4.jar"/>
+        <include name="jackson-annotations-2.4.4.jar"/>
+        <include name="jackson-databind-2.4.4.jar"/>
+        <include name="jackson-dataformat-smile-2.4.4.jar"/>
+        <include name="jackson-dataformat-xml-2.4.4.jar"/>
+        <include name="jackson-dataformat-yaml-2.4.4.jar"/>
+        <include name="jackson-dataformat-csv-2.4.4.jar"/>
+        <include name="jackson-dataformat-cbor-2.4.4.jar"/>
         <include name="slf4j-api-1.6.4.jar"/>
         <include name="org.restlet.jar"/>
         <include name="org.restlet.ext.jackson.jar"/>
@@ -213,7 +214,10 @@
 				    <include name="**/*Test*.java"/>
                     <exclude name="**/core/test/**"/>
                     <exclude name="**/core/module/**"/>
-                </fileset>
+		    <exclude name="**/util/FutureTestUtils.java"/> <!-- No tests here. Used within others that have Futures -->
+		    <exclude name="**/test/FloodlightTestCase.java"/> <!-- Base class for other tests. Can't run it individually. -->
+                    <exclude name="**/core/internal/OFSwitchHandlerTestBase.java"/> <!-- Abstract class used by protocol version handshake handlers. -->
+		</fileset>
             </batchtest>
         </junit>
         <fail if="junit.failure" message="Unit test(s) failed.  See reports!"/>
diff --git a/lib/jackson-annotations-2.1.4.jar b/lib/jackson-annotations-2.1.4.jar
deleted file mode 100644
index 143edf44b0daa4cef1a452ecccac21aee22a8d77..0000000000000000000000000000000000000000
Binary files a/lib/jackson-annotations-2.1.4.jar and /dev/null differ
diff --git a/lib/jackson-annotations-2.4.4.jar b/lib/jackson-annotations-2.4.4.jar
new file mode 100644
index 0000000000000000000000000000000000000000..4779188a9f0f306169d566bf7e73570e0bf3e45b
Binary files /dev/null and b/lib/jackson-annotations-2.4.4.jar differ
diff --git a/lib/jackson-core-2.1.4.jar b/lib/jackson-core-2.1.4.jar
deleted file mode 100644
index 0f144685f7140d2694eeba5609322b4cd79f0bf8..0000000000000000000000000000000000000000
Binary files a/lib/jackson-core-2.1.4.jar and /dev/null differ
diff --git a/lib/jackson-core-2.4.4.jar b/lib/jackson-core-2.4.4.jar
new file mode 100644
index 0000000000000000000000000000000000000000..da2a877f186a999df8fbafaac5c59316d3fb70b4
Binary files /dev/null and b/lib/jackson-core-2.4.4.jar differ
diff --git a/lib/jackson-databind-2.1.4.jar b/lib/jackson-databind-2.1.4.jar
deleted file mode 100644
index ce125d1df292a1d8fa4b86cf39770d0d9c4636cc..0000000000000000000000000000000000000000
Binary files a/lib/jackson-databind-2.1.4.jar and /dev/null differ
diff --git a/lib/jackson-databind-2.4.4.jar b/lib/jackson-databind-2.4.4.jar
new file mode 100644
index 0000000000000000000000000000000000000000..e945e5e800666e0c5f99e9571185327f663d73ce
Binary files /dev/null and b/lib/jackson-databind-2.4.4.jar differ
diff --git a/lib/jackson-dataformat-cbor-2.4.4.jar b/lib/jackson-dataformat-cbor-2.4.4.jar
new file mode 100644
index 0000000000000000000000000000000000000000..8a536d2b806a5a50473e602a932f1a2527b3a604
Binary files /dev/null and b/lib/jackson-dataformat-cbor-2.4.4.jar differ
diff --git a/lib/jackson-dataformat-csv-2.1.4.jar b/lib/jackson-dataformat-csv-2.1.4.jar
deleted file mode 100644
index e97503cb161c44ca54d096ec669b707e522f0004..0000000000000000000000000000000000000000
Binary files a/lib/jackson-dataformat-csv-2.1.4.jar and /dev/null differ
diff --git a/lib/jackson-dataformat-csv-2.4.4.jar b/lib/jackson-dataformat-csv-2.4.4.jar
new file mode 100644
index 0000000000000000000000000000000000000000..f561dc10ff44e60210f04315f508bf76d1843d1c
Binary files /dev/null and b/lib/jackson-dataformat-csv-2.4.4.jar differ
diff --git a/lib/jackson-dataformat-smile-2.1.4.jar b/lib/jackson-dataformat-smile-2.1.4.jar
deleted file mode 100644
index e907c7726c5679a572e098293581bba28ac63fa1..0000000000000000000000000000000000000000
Binary files a/lib/jackson-dataformat-smile-2.1.4.jar and /dev/null differ
diff --git a/lib/jackson-dataformat-smile-2.4.4.jar b/lib/jackson-dataformat-smile-2.4.4.jar
new file mode 100644
index 0000000000000000000000000000000000000000..2a6b285190944cd6536619e0caca6aca4af18067
Binary files /dev/null and b/lib/jackson-dataformat-smile-2.4.4.jar differ
diff --git a/lib/jackson-dataformat-xml-2.1.4.jar b/lib/jackson-dataformat-xml-2.1.4.jar
deleted file mode 100644
index 15c82e69c86703363e16d47cb8cb3c3de1919c43..0000000000000000000000000000000000000000
Binary files a/lib/jackson-dataformat-xml-2.1.4.jar and /dev/null differ
diff --git a/lib/jackson-dataformat-xml-2.4.4.jar b/lib/jackson-dataformat-xml-2.4.4.jar
new file mode 100644
index 0000000000000000000000000000000000000000..8f0fa34d09634e02bda7dc0fe7938b8e58fdb1a2
Binary files /dev/null and b/lib/jackson-dataformat-xml-2.4.4.jar differ
diff --git a/lib/jackson-dataformat-yaml-2.1.4.jar b/lib/jackson-dataformat-yaml-2.1.4.jar
deleted file mode 100644
index 444ef88653e9574fb90246679835448e02170ee8..0000000000000000000000000000000000000000
Binary files a/lib/jackson-dataformat-yaml-2.1.4.jar and /dev/null differ
diff --git a/lib/jackson-dataformat-yaml-2.4.4.jar b/lib/jackson-dataformat-yaml-2.4.4.jar
new file mode 100644
index 0000000000000000000000000000000000000000..7770cb918467a8141c8d169f09042a5112e5f36d
Binary files /dev/null and b/lib/jackson-dataformat-yaml-2.4.4.jar differ
diff --git a/lib/openflowj-0.9.0-SNAPSHOT-javadoc.jar b/lib/openflowj-0.9.0-SNAPSHOT-javadoc.jar
index 336684ee97dcfdb417dff49df9600548498c7163..3b2cd87c8da1a2ba622894c2241b4321b59e6ce2 100644
Binary files a/lib/openflowj-0.9.0-SNAPSHOT-javadoc.jar and b/lib/openflowj-0.9.0-SNAPSHOT-javadoc.jar differ
diff --git a/lib/openflowj-0.9.0-SNAPSHOT-sources.jar b/lib/openflowj-0.9.0-SNAPSHOT-sources.jar
index a4086ecfd7d6602661b35f1480d95cf0a0554739..abdf436f91750e82a1e50f3376105bf590526477 100644
Binary files a/lib/openflowj-0.9.0-SNAPSHOT-sources.jar and b/lib/openflowj-0.9.0-SNAPSHOT-sources.jar differ
diff --git a/lib/openflowj-0.9.0-SNAPSHOT.jar b/lib/openflowj-0.9.0-SNAPSHOT.jar
index 929c5bdbf4191b8ffb2c3dd042d882e021bd6c70..2a2140a0038065ee1bc875723a42ae9b9cf8ece0 100644
Binary files a/lib/openflowj-0.9.0-SNAPSHOT.jar and b/lib/openflowj-0.9.0-SNAPSHOT.jar differ
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..05c0b1784d45c95844c78a6f0f48256f030475db
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,233 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<groupId>org.projectfloodlight</groupId>
+	<artifactId>floodlight</artifactId>
+	<version>1.2-SNAPSHOT</version>
+	<name>floodlight</name>
+	<repositories>
+		<repository>
+			<id>maven-restlet</id>
+			<name>Public online Restlet repository</name>
+			<url>http://maven.restlet.com</url>
+		</repository>
+		<repository>
+			<id>sonatype-oss-snapshot</id>
+			<name>Sonatype OSS snapshot repository</name>
+			<url>https://oss.sonatype.org/content/repositories/snapshots</url>
+		</repository>
+	</repositories>
+	<build>
+		<plugins>
+			<plugin>
+				<artifactId>maven-compiler-plugin</artifactId>
+				<version>3.0</version>
+				<configuration>
+					<source>1.7</source>
+					<target>1.7</target>
+				</configuration>
+			</plugin>
+			<plugin>
+				<groupId>org.codehaus.mojo</groupId>
+				<artifactId>build-helper-maven-plugin</artifactId>
+				<version>1.7</version>
+				<executions>
+					<execution>
+						<id>add-source</id>
+						<phase>generate-sources</phase>
+						<goals>
+							<goal>add-source</goal>
+						</goals>
+						<configuration>
+							<sources>
+								<source>lib/gen-java</source>
+								<source>src/main/python</source>
+							</sources>
+						</configuration>
+					</execution>
+				</executions>
+			</plugin>
+		</plugins>
+		<pluginManagement>
+			<plugins>
+				<!--This plugin's configuration is used to store Eclipse m2e settings 
+					only. It has no influence on the Maven build itself. -->
+				<plugin>
+					<groupId>org.eclipse.m2e</groupId>
+					<artifactId>lifecycle-mapping</artifactId>
+					<version>1.0.0</version>
+					<configuration>
+						<lifecycleMappingMetadata>
+							<pluginExecutions>
+								<pluginExecution>
+									<pluginExecutionFilter>
+										<groupId>
+											org.codehaus.mojo
+										</groupId>
+										<artifactId>
+											build-helper-maven-plugin
+										</artifactId>
+										<versionRange>
+											[1.7,)
+										</versionRange>
+										<goals>
+											<goal>add-source</goal>
+										</goals>
+									</pluginExecutionFilter>
+									<action>
+										<ignore />
+									</action>
+								</pluginExecution>
+							</pluginExecutions>
+						</lifecycleMappingMetadata>
+					</configuration>
+				</plugin>
+			</plugins>
+		</pluginManagement>
+		<resources>
+			<resource>
+				<directory>${basedir}</directory>
+				<includes>
+					<include>LICENSE.txt</include>
+					<include>NOTICE.txt</include>
+					<include>README.md</include>
+				</includes>
+			</resource>
+		</resources>
+	</build>
+	<dependencies>
+		<dependency>
+			<groupId>com.google.guava</groupId>
+			<artifactId>guava</artifactId>
+			<version>13.0.1</version>
+		</dependency>
+		<dependency>
+			<groupId>ch.qos.logback</groupId>
+			<artifactId>logback-classic</artifactId>
+			<version>1.0.0</version>
+		</dependency>
+		<dependency>
+			<groupId>org.slf4j</groupId>
+			<artifactId>slf4j-api</artifactId>
+			<version>1.6.4</version>
+		</dependency>
+		<dependency>
+			<groupId>org.restlet.jse</groupId>
+			<artifactId>org.restlet</artifactId>
+			<version>${lib-restlet-version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.restlet.jse</groupId>
+			<artifactId>org.restlet.ext.jackson</artifactId>
+			<version>${lib-restlet-version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.restlet.jse</groupId>
+			<artifactId>org.restlet.ext.simple</artifactId>
+			<version>${lib-restlet-version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.restlet.jse</groupId>
+			<artifactId>org.restlet.ext.slf4j</artifactId>
+			<version>${lib-restlet-version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.restlet.jse</groupId>
+			<artifactId>org.restlet.ext.jsslutils</artifactId>
+			<version>${lib-restlet-version}</version>
+		</dependency>
+		<dependency>
+			<groupId>args4j</groupId>
+			<artifactId>args4j</artifactId>
+			<version>2.0.16</version>
+		</dependency>
+		<dependency>
+			<groupId>com.googlecode.concurrentlinkedhashmap</groupId>
+			<artifactId>concurrentlinkedhashmap-lru</artifactId>
+			<version>1.2</version>
+		</dependency>
+		<dependency>
+			<groupId>org.python</groupId>
+			<artifactId>jython-standalone</artifactId>
+			<version>2.5.2</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.thrift</groupId>
+			<artifactId>libthrift</artifactId>
+			<version>0.9.0</version>
+		</dependency>
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<version>4.8.2</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.easymock</groupId>
+			<artifactId>easymock</artifactId>
+			<version>3.1</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>cglib</groupId>
+			<artifactId>cglib-nodep</artifactId>
+			<version>2.2.2</version>
+		</dependency>
+		<dependency>
+			<groupId>org.objenesis</groupId>
+			<artifactId>objenesis</artifactId>
+			<version>1.2</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.derby</groupId>
+			<artifactId>derby</artifactId>
+			<version>10.9.1.0</version>
+		</dependency>
+		<dependency>
+			<groupId>com.google.code.findbugs</groupId>
+			<artifactId>jsr305</artifactId>
+			<version>2.0.1</version>
+		</dependency>
+		<dependency>
+			<groupId>com.google.code.findbugs</groupId>
+			<artifactId>annotations</artifactId>
+			<version>2.0.1</version>
+		</dependency>
+		<dependency>
+			<groupId>org.hamcrest</groupId>
+			<artifactId>hamcrest-core</artifactId>
+			<version>${lib-hamcrest-version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.hamcrest</groupId>
+			<artifactId>hamcrest-integration</artifactId>
+			<version>${lib-hamcrest-version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.hamcrest</groupId>
+			<artifactId>hamcrest-library</artifactId>
+			<version>${lib-hamcrest-version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.projectfloodlight</groupId>
+			<artifactId>openflowj</artifactId>
+			<version>0.9.0-SNAPSHOT</version>
+		</dependency>
+		<dependency>
+			<groupId>io.netty</groupId>
+			<artifactId>netty</artifactId>
+			<version>3.9.0.Final</version>
+		</dependency>
+		<dependency>
+			<groupId>com.fasterxml.jackson.dataformat</groupId>
+			<artifactId>jackson-dataformat-cbor</artifactId>
+			<version>${lib-jackson-version}</version>
+		</dependency>
+	</dependencies>
+	<properties>
+		<lib-restlet-version>2.3.2</lib-restlet-version>
+		<lib-jackson-version>2.4.4</lib-jackson-version>
+		<lib-hamcrest-version>1.3</lib-hamcrest-version>
+	</properties>
+</project>
diff --git a/src/main/java/net/floodlightcontroller/accesscontrollist/ACL.java b/src/main/java/net/floodlightcontroller/accesscontrollist/ACL.java
index 36b13123c233c2728ec87083fb608d651bd261f5..edfc1015f656cde20bf9589091547fd582ed451e 100644
--- a/src/main/java/net/floodlightcontroller/accesscontrollist/ACL.java
+++ b/src/main/java/net/floodlightcontroller/accesscontrollist/ACL.java
@@ -24,10 +24,10 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.TreeMap;
 
 import net.floodlightcontroller.accesscontrollist.ACLRule.Action;
 import net.floodlightcontroller.accesscontrollist.ap.AP;
@@ -54,226 +54,254 @@ import org.slf4j.LoggerFactory;
 
 public class ACL implements IACLService, IFloodlightModule, IDeviceListener {
 
-	// service modules needed
 	protected IRestApiService restApi;
 	protected IDeviceService deviceManager;
 	protected IStorageSourceService storageSource;
 	protected static Logger logger;
-	
-	private APManager apManager;
 
-	// variable used
+	private APManager apManager;
 	private int lastRuleId = 1; // rule id counter
-	private List<ACLRule> ruleSet;
+	private Map<Integer, ACLRule> aclRules;
 	private Map<String, Integer> dpid2FlowPriority;
 	private Map<Integer, Set<String>> ruleId2Dpid;
 	private Map<Integer, Set<String>> ruleId2FlowName;
+	private Map<Integer, List<Integer>> deny2Allow;
+
+	private final int DEFAULT_PRIORITY = 30000;
 
 	/**
-	 * used by REST API to query ACL rules
+	 * Checks if an existing ACL rule already works in a given switch.
 	 */
+	private boolean checkIfRuleWorksInSwitch(int ruleId, String dpid) {
+		return ruleId2Dpid.containsKey(ruleId)
+				&& ruleId2Dpid.get(ruleId).contains(dpid);
+	}
+
+	/**
+	 * Adds a new mapping from ACL rule to ACL flow.
+	 */
+	private void addRuleToFlowMapping(int ruleId, String flowName) {
+		if (!ruleId2FlowName.containsKey(ruleId)) {
+			ruleId2FlowName.put(ruleId, new HashSet<String>());
+		}
+		ruleId2FlowName.get(ruleId).add(flowName);
+	}
+
+	/**
+	 * Adds a new mapping from ACL rule to switch.
+	 */
+	private void addRuleToSwitchMapping(int ruleId, String dpid) {
+		if (!ruleId2Dpid.containsKey(ruleId)) {
+			ruleId2Dpid.put(ruleId, new HashSet<String>());
+		}
+		ruleId2Dpid.get(ruleId).add(dpid);
+	}
+
+	/**
+	 * Gets the current priority for new ACL flow by device id.
+	 */
+	private int getPriorityBySwitch(String dpid) {
+		if (!dpid2FlowPriority.containsKey(dpid)) {
+			dpid2FlowPriority.put(dpid, DEFAULT_PRIORITY - 1);
+			return DEFAULT_PRIORITY;
+		} else {
+			int priority = dpid2FlowPriority.get(dpid);
+			dpid2FlowPriority.put(dpid, priority - 1);
+			return priority;
+		}
+	}
+
 	@Override
 	public List<ACLRule> getRules() {
-		return this.ruleSet;
+		return new ArrayList<ACLRule>(aclRules.values());
 	}
 
 	/**
-	 * check if the new rule matches an existing rule
+	 * Checks if the new ACL rule matches an existing rule. If existing allowing
+	 * rules matches the new denying rule, store the mappings.
+	 * 
+	 * @return true if the new ACL rule matches an existing rule, false
+	 *         otherwise
 	 */
 	private boolean checkRuleMatch(ACLRule newRule) {
-		Iterator<ACLRule> iter = ruleSet.iterator();
-		while (iter.hasNext()) {
-			ACLRule existingRule = iter.next();
-			if(newRule.match(existingRule)){
-				logger.error("existing rule: " + existingRule);
+		List<Integer> allowRuleList = new ArrayList<>();
+		for (ACLRule existingRule : getRules()) {
+			if (newRule.match(existingRule)) {
 				return true;
 			}
+
+			if (existingRule.getAction() == Action.ALLOW
+					&& newRule.getAction() == Action.DENY) {
+				if (existingRule.match(newRule)) {
+					allowRuleList.add(existingRule.getId());
+				}
+			}
 		}
+		deny2Allow.put(newRule.getId(), allowRuleList);
 		return false;
 	}
-	
-	/**
-	 * used by REST API to add ACL rule
-	 * @return if the new ACL rule is added successfully
-	 */
+
 	@Override
 	public boolean addRule(ACLRule rule) {
-
-		if(checkRuleMatch(rule)){
+		rule.setId(lastRuleId++);
+		if (checkRuleMatch(rule)) {
+			lastRuleId--;
 			return false;
 		}
-		
-		rule.setId(lastRuleId++);
-		this.ruleSet.add(rule);
-		logger.info("No.{} ACL rule added.", rule.getId());
-		enforceAddedRule(rule);
+		aclRules.put(rule.getId(), rule);
+		logger.info("ACL rule(id:{}) is added.", rule.getId());
+		if (rule.getAction() != Action.ALLOW) {
+			enforceAddedRule(rule);
+		}
 		return true;
 	}
 
-	/**
-	 * used by REST API to remove ACL rule
-	 */
 	@Override
-	public void removeRule(int ruleid) {
-
-		Iterator<ACLRule> iter = this.ruleSet.iterator();
-		while (iter.hasNext()) {
-			ACLRule rule = iter.next();
-			if (rule.getId() == ruleid) {
				iter.remove();
-				break;
-			}
-		}
-
-		logger.info("No.{} ACL rule removed.", ruleid);
-		enforceRemovedRule(ruleid);
+	public void removeRule(int ruleId) {
+		aclRules.remove(ruleId);
+		logger.info("ACL rule(id:{}) is removed.", ruleId);
+		enforceRemovedRule(ruleId);
 	}
 
-	/**
-	 * used by REST API to clear ACL
-	 */
 	@Override
 	public void removeAllRules() {
-		
 		this.lastRuleId = 1;
-		this.ruleSet = new ArrayList<ACLRule>();
-		this.dpid2FlowPriority = new HashMap<String, Integer>();
-		this.ruleId2Dpid = new HashMap<Integer, Set<String>>();
-
-		Iterator<Integer> ruleIdIter = ruleId2FlowName.keySet().iterator();
-		while (ruleIdIter.hasNext()) {
-			int ruleId = ruleIdIter.next();
-			Set<String> flowNameSet = ruleId2FlowName.get(ruleId);
-			logger.info("No.{} ACL rule removed.", ruleId);
+		this.aclRules = new TreeMap<>();
+		this.dpid2FlowPriority = new HashMap<>();
+		this.ruleId2Dpid = new HashMap<>();
+		this.deny2Allow = new HashMap<>();
+
+		for (Set<String> flowNameSet : ruleId2FlowName.values()) {
 			for (String flowName : flowNameSet) {
-				removeFlow(flowName);
-				logger.info("ACL flow {} removed.", flowName);
+				storageSource.deleteRowAsync("controller_staticflowtableentry",
+						flowName);
+				logger.debug("ACL flow(id:{}) is removed.", flowName);
 			}
 		}
-		this.ruleId2FlowName = new HashMap<Integer, Set<String>>();
+		this.ruleId2FlowName = new HashMap<>();
 	}
 
 	/**
-	 * enforce new added rule
+	 * Enforces denying ACL rule by ACL flow.
 	 */
-	private void enforceAddedRule(ACLRule rule) {
-
+	private void enforceAddedRule(ACLRule denyRule) {
 		Set<String> dpidSet;
-		if (rule.getNw_src() != null) {
-			dpidSet = apManager.getDpidSet(rule.getNw_src_prefix(),rule.getNw_src_maskbits());
+		if (denyRule.getNw_src() != null) {
+			dpidSet = apManager.getDpidSet(denyRule.getNw_src_prefix(),
+					denyRule.getNw_src_maskbits());
 		} else {
-			dpidSet = apManager.getDpidSet(rule.getNw_dst_prefix(),rule.getNw_dst_maskbits());
+			dpidSet = apManager.getDpidSet(denyRule.getNw_dst_prefix(),
+					denyRule.getNw_dst_maskbits());
 		}
 
-		Iterator<String> dpidIter = dpidSet.iterator();
-		Set<String> nameSet = new HashSet<String>();
-
-		while (dpidIter.hasNext()) {
-			String dpid = dpidIter.next();
-			String flowName = "ACLRule_" + rule.getId() + "_" + dpid;
-			generateFlow(rule, dpid, flowName);
-			nameSet.add(flowName);
+		for (String dpid : dpidSet) {
+			String flowName;
+			List<Integer> allowRuleList = deny2Allow.get(denyRule.getId());
+			for (int allowRuleId : allowRuleList) {
+				flowName = "ACLRule_" + allowRuleId + "_" + dpid;
+				generateFlow(aclRules.get(allowRuleId), dpid, flowName);
+			}
+			flowName = "ACLRule_" + denyRule.getId() + "_" + dpid;
+			generateFlow(denyRule, dpid, flowName);
 		}
-		ruleId2FlowName.put(rule.getId(), nameSet);
-		ruleId2Dpid.put(rule.getId(), dpidSet);
 	}
 
 	/**
-	 * enforce removed rule
+	 * Enforces removing an existing ACL rule.
 	 */
 	private void enforceRemovedRule(int ruleId) {
-
-		Set<String> flowEntryName = ruleId2FlowName.get(ruleId);
-		Iterator<String> iter = flowEntryName.iterator();
-		while (iter.hasNext()) {
-			String name = iter.next();
-			removeFlow(name);
-			logger.info("ACL flow " + name + " removed.");
+		if (ruleId2FlowName.containsKey(ruleId)) {
+			for (String flowName : ruleId2FlowName.get(ruleId)) {
+				storageSource.deleteRowAsync("controller_staticflowtableentry",
+						flowName);
+				logger.debug("ACL flow(id:{}) is removed.", flowName);
+			}
+			ruleId2FlowName.remove(ruleId);
 		}
-
+		ruleId2Dpid.remove(ruleId);
+		deny2Allow.remove(ruleId);
 	}
-		
+
 	/**
-	 * generate and push ACL flow entry
+	 * Generates ACL flow rule according to ACL rule 
+	 * and installs it into switch.
 	 */
 	private void generateFlow(ACLRule rule, String dpid, String flowName) {
-
-		int priority;
-		// get priority for the new flow entry
-		if (dpid2FlowPriority.get(dpid) == null) {
-			dpid2FlowPriority.put(dpid, 30000);
-			priority = 30000;
-		} else {
-			priority = dpid2FlowPriority.get(dpid);
+		if (rule == null || checkIfRuleWorksInSwitch(rule.getId(), dpid)) {
+			return;
 		}
 
+		int priority = getPriorityBySwitch(dpid);
 		if (rule.getNw_src() != null) {
-			
-			HashMap<String,Object> flow = new HashMap<String,Object>();
-	        
+
+			HashMap<String, Object> flow = new HashMap<String, Object>();
+
 			flow.put(StaticFlowEntryPusher.COLUMN_SWITCH, dpid);
 			flow.put(StaticFlowEntryPusher.COLUMN_NAME, flowName);
-			flow.put(StaticFlowEntryPusher.COLUMN_ACTIVE, Boolean.toString(true));
+			flow.put(StaticFlowEntryPusher.COLUMN_ACTIVE,
+					Boolean.toString(true));
 			flow.put(StaticFlowEntryPusher.COLUMN_COOKIE, "0");
-			flow.put(StaticFlowEntryPusher.COLUMN_PRIORITY, Integer.toString(priority));
-			dpid2FlowPriority.put(dpid, --priority);
-			
-			
+			flow.put(StaticFlowEntryPusher.COLUMN_PRIORITY,
+					Integer.toString(priority));
+
 			flow.put(StaticFlowEntryPusher.COLUMN_DL_TYPE, "2048");
 			flow.put(StaticFlowEntryPusher.COLUMN_NW_SRC, rule.getNw_src());
-			
-			// process for the nw_dst attribute
+
 			if (rule.getNw_dst() != null) {
 				flow.put(StaticFlowEntryPusher.COLUMN_NW_DST, rule.getNw_dst());
 			}
 			if (rule.getNw_proto() != 0) {
-				flow.put(StaticFlowEntryPusher.COLUMN_NW_PROTO, Integer.toString(rule.getNw_proto()));
+				flow.put(StaticFlowEntryPusher.COLUMN_NW_PROTO,
+						Integer.toString(rule.getNw_proto()));
 			}
 			if (rule.getAction() == Action.ALLOW) {
-				flow.put(StaticFlowEntryPusher.COLUMN_ACTIONS, "output=controller");
+				flow.put(StaticFlowEntryPusher.COLUMN_ACTIONS,
+						"output=controller");
 			}
 			if (rule.getTp_dst() != 0) {
-				flow.put(StaticFlowEntryPusher.COLUMN_TP_DST, Integer.toString(rule.getTp_dst()));
+				flow.put(StaticFlowEntryPusher.COLUMN_TP_DST,
+						Integer.toString(rule.getTp_dst()));
 			}
-	        
-	        storageSource.insertRowAsync(StaticFlowEntryPusher.TABLE_NAME, flow);
-			
+
+			storageSource
+					.insertRowAsync(StaticFlowEntryPusher.TABLE_NAME, flow);
+
 		} else {
-			
-			HashMap<String,Object> flow = new HashMap<String,Object>();
-	        
+
+			HashMap<String, Object> flow = new HashMap<String, Object>();
+
 			flow.put(StaticFlowEntryPusher.COLUMN_SWITCH, dpid);
 			flow.put(StaticFlowEntryPusher.COLUMN_NAME, flowName);
-			flow.put(StaticFlowEntryPusher.COLUMN_ACTIVE, Boolean.toString(true));
+			flow.put(StaticFlowEntryPusher.COLUMN_ACTIVE,
+					Boolean.toString(true));
 			flow.put(StaticFlowEntryPusher.COLUMN_COOKIE, "0");
-			flow.put(StaticFlowEntryPusher.COLUMN_PRIORITY, Integer.toString(priority));
-			dpid2FlowPriority.put(dpid, --priority);
-			
+			flow.put(StaticFlowEntryPusher.COLUMN_PRIORITY,
+					Integer.toString(priority));
+
 			flow.put(StaticFlowEntryPusher.COLUMN_DL_TYPE, "2048");
 			flow.put(StaticFlowEntryPusher.COLUMN_NW_DST, rule.getNw_dst());
 
 			if (rule.getNw_proto() != 0) {
-				flow.put(StaticFlowEntryPusher.COLUMN_NW_PROTO, Integer.toString(rule.getNw_proto()));
+				flow.put(StaticFlowEntryPusher.COLUMN_NW_PROTO,
+						Integer.toString(rule.getNw_proto()));
 			}
 			if (rule.getAction() == Action.ALLOW) {
-				flow.put(StaticFlowEntryPusher.COLUMN_ACTIONS, "output=controller");
+				flow.put(StaticFlowEntryPusher.COLUMN_ACTIONS,
+						"output=controller");
 			}
 			if (rule.getTp_dst() != 0) {
-				flow.put(StaticFlowEntryPusher.COLUMN_TP_DST, Integer.toString(rule.getTp_dst()));
+				flow.put(StaticFlowEntryPusher.COLUMN_TP_DST,
+						Integer.toString(rule.getTp_dst()));
 			}
-	        
-	        storageSource.insertRowAsync(StaticFlowEntryPusher.TABLE_NAME, flow);
-	        
-		}
-		logger.info("ACL flow " + flowName + " added in " + dpid);
-	}
 
-	/**
-	 * remove ACL flow entry
-	 */
-	private void removeFlow(String name) {
-		storageSource.deleteRowAsync("controller_staticflowtableentry", name);
-	}
+			storageSource
+					.insertRowAsync(StaticFlowEntryPusher.TABLE_NAME, flow);
 
+		}
+		addRuleToSwitchMapping(rule.getId(), dpid);
+		addRuleToFlowMapping(rule.getId(), flowName);
+		logger.debug("ACL flow(id:{}) is added in {}.", flowName, dpid);
+	}
 
 	@Override
 	public Collection<Class<? extends IFloodlightService>> getModuleServices() {
@@ -306,11 +334,12 @@ public class ACL implements IACLService, IFloodlightModule, IDeviceListener {
 		logger = LoggerFactory.getLogger(ACL.class);
 		storageSource = context.getServiceImpl(IStorageSourceService.class);
 
-		ruleSet = new ArrayList<ACLRule>();
+		aclRules = new TreeMap<>();
 		apManager = new APManager();
-		ruleId2FlowName = new HashMap<Integer, Set<String>>();
-		ruleId2Dpid =  new HashMap<Integer, Set<String>>();
-		dpid2FlowPriority = new HashMap<String, Integer>();
+		ruleId2FlowName = new HashMap<>();
+		ruleId2Dpid = new HashMap<>();
+		dpid2FlowPriority = new HashMap<>();
+		deny2Allow = new HashMap<>();
 	}
 
 	@Override
@@ -320,60 +349,55 @@ public class ACL implements IACLService, IFloodlightModule, IDeviceListener {
 		deviceManager.addListener(this);
 	}
 
-	/**
-	 * listen for new device
-	 */
 	@Override
 	public void deviceAdded(IDevice device) {
 		SwitchPort[] switchPort = device.getAttachmentPoints();
 		IPv4Address[] ips = device.getIPv4Addresses();
-		if(ips.length == 0){
+		if (ips.length == 0) {
 			// A new no-ip device added
 			return;
 		}
-		String dpid = HexString.toHexString(switchPort[0].getSwitchDPID().getLong());
+
+		String dpid = HexString.toHexString(switchPort[0].getSwitchDPID()
+				.getLong());
 		String ip = IPv4.fromIPv4Address(ips[0].getInt());
-		logger.info("New AP added. [dpid:" + dpid + " ip:" + ip + "]");
+		logger.debug("AP(dpid:{},ip:{}) is added", dpid, ip);
 
-		AP ap = new AP(ip,dpid);
+		AP ap = new AP(ip, dpid);
 		apManager.addAP(ap);
 		processAPAdded(ap);
 	}
 
 	/**
-	 * push ACL flow given the new device
+	 * Generates new ACL flow when a new device appears
+	 * and existing ACL rules denies its traffic.
 	 */
 	private void processAPAdded(AP ap) {
-
 		String dpid = ap.getDpid();
 		int ip = IPv4.toIPv4Address(ap.getIp());
 
-		Iterator<ACLRule> iter = this.ruleSet.iterator();
-		while (iter.hasNext()) {
-			ACLRule rule = iter.next();
-			if (rule.getNw_src() != null) {
-				if (IPAddressUtil.containIP(rule.getNw_src_prefix(),
-						rule.getNw_src_maskbits(), ip)) {
-					// check if there is a flow entry in the switch for the rule
-					if (ruleId2Dpid.get(rule.getId()).contains(dpid)) {
-						continue;
+		for (ACLRule rule : getRules()) {
+			if (rule.getAction() != Action.ALLOW) {
+				if (rule.getNw_src() != null) {
+					if (IPAddressUtil.containIP(rule.getNw_src_prefix(),
+							rule.getNw_src_maskbits(), ip)) {
+						if (checkIfRuleWorksInSwitch(rule.getId(), dpid)) {
+							continue;
+						}
+						String flowName = "ACLRule_" + rule.getId() + "_"
+								+ dpid;
+						generateFlow(rule, dpid, flowName);
 					}
-					String flowName = "ACLRule_" + rule.getId() + "_" + dpid;
-					ruleId2FlowName.get(rule.getId()).add(flowName);
-					ruleId2Dpid.get(rule.getId()).add(dpid);
-					generateFlow(rule, dpid, flowName);
-				}
-			} else {
-				if (IPAddressUtil.containIP(rule.getNw_dst_prefix(),
-						rule.getNw_dst_maskbits(), ip)) {
-					// check if there is a flow entry in the switch for the rule
-					if (ruleId2Dpid.get(rule.getId()).contains(dpid)) {
-						continue;
+				} else {
+					if (IPAddressUtil.containIP(rule.getNw_dst_prefix(),
+							rule.getNw_dst_maskbits(), ip)) {
+						if (checkIfRuleWorksInSwitch(rule.getId(), dpid)) {
+							continue;
+						}
+						String flowName = "ACLRule_" + rule.getId() + "_"
+								+ dpid;
+						generateFlow(rule, dpid, flowName);
 					}
-					String flowName = "ACLRule_" + rule.getId() + "_" + dpid;
-					ruleId2FlowName.get(rule.getId()).add(flowName);
-					ruleId2Dpid.get(rule.getId()).add(dpid);
-					generateFlow(rule, dpid, flowName);
 				}
 			}
 		}
@@ -389,24 +413,30 @@ public class ACL implements IACLService, IFloodlightModule, IDeviceListener {
 
 	}
 
+	@Override
+	public void deviceIPV6AddrChanged(IDevice device) {
+		logger.debug("IPv6 not implemented in ACL. Device changed: {}", device.toString());
+	}
+	
 	@Override
 	public void deviceIPV4AddrChanged(IDevice device) {
-		
+
 		SwitchPort[] switchPort = device.getAttachmentPoints();
 		IPv4Address[] ips = device.getIPv4Addresses();
-		
-		String dpid = HexString.toHexString(switchPort[0].getSwitchDPID().getLong());
+
+		String dpid = HexString.toHexString(switchPort[0].getSwitchDPID()
+				.getLong());
 		String ip = null;
-		
-		// some device may first appear with no IP address(default set to 0.0.0.0), ignore it
-		for(IPv4Address i : ips){
-			if(i.getInt() != 0){
+		// some device may first appear with no IP address(default set to
+		// 0.0.0.0), ignore it
+		for (IPv4Address i : ips) {
+			if (i.getInt() != 0) {
 				ip = IPv4.fromIPv4Address(i.getInt());
 				break;
 			}
 		}
-		
-		logger.info("New AP added. [dpid:" + dpid + " ip:" + ip + "]");
+
+		logger.debug("AP(dpid:{},ip:{}) is added", dpid, ip);
 		AP ap = new AP(ip, dpid);
 		apManager.addAP(ap);
 		processAPAdded(ap);
@@ -419,7 +449,7 @@ public class ACL implements IACLService, IFloodlightModule, IDeviceListener {
 
 	@Override
 	public String getName() {
-		return null;
+		return "ACL manager";
 	}
 
 	@Override
diff --git a/src/main/java/net/floodlightcontroller/accesscontrollist/FlowPusher.java b/src/main/java/net/floodlightcontroller/accesscontrollist/FlowPusher.java
deleted file mode 100644
index 3ba9e3da0c649c86405a324777aa1742759b8fd2..0000000000000000000000000000000000000000
--- a/src/main/java/net/floodlightcontroller/accesscontrollist/FlowPusher.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/**
- *    Copyright 2015, Big Switch Networks, Inc.
- *    Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China 
- *    Advisers: Keqiu Li and Heng Qi 
- *    This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002) 
- *    and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute.
- *    
- *    Licensed under the Apache License, Version 2.0 (the "License"); you may 
- *    not use this file except in compliance with the License. You may obtain
- *    a copy of the License at
- *    
- *         http://www.apache.org/licenses/LICENSE-2.0 
- *    
- *    Unless required by applicable law or agreed to in writing, software 
- *    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- *    License for the specific language governing permissions and limitations
- *    under the License.
- **/
-
-package net.floodlightcontroller.accesscontrollist;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-
-import net.floodlightcontroller.core.IFloodlightProviderService;
-import net.floodlightcontroller.core.module.FloodlightModuleContext;
-import net.floodlightcontroller.core.module.FloodlightModuleException;
-import net.floodlightcontroller.core.module.IFloodlightModule;
-import net.floodlightcontroller.core.module.IFloodlightService;
-import net.floodlightcontroller.staticflowentry.IStaticFlowEntryPusherService;
-import net.floodlightcontroller.staticflowentry.StaticFlowEntryPusher;
-import net.floodlightcontroller.storage.IStorageSourceService;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class FlowPusher implements IFloodlightModule {
-
-	protected IFloodlightProviderService floodlightProvider;
-	protected static Logger logger;
-	protected IStorageSourceService storageSource;
-	protected IStaticFlowEntryPusherService sfp;
-
-	@Override
-	public Collection<Class<? extends IFloodlightService>> getModuleServices() {
-		return null;
-	}
-
-	@Override
-	public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
-		return null;
-	}
-
-	@Override
-	public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
-		Collection<Class<? extends IFloodlightService>> l = new ArrayList<Class<? extends IFloodlightService>>();
-		l.add(IFloodlightProviderService.class);
-		l.add(IStaticFlowEntryPusherService.class);
-        l.add(IStorageSourceService.class);
-		return l;
-	}
-
-	@Override
-	public void init(FloodlightModuleContext context)
-			throws FloodlightModuleException {
-		floodlightProvider = context
-				.getServiceImpl(IFloodlightProviderService.class);
-		storageSource = context.getServiceImpl(IStorageSourceService.class);
-		sfp = context.getServiceImpl(IStaticFlowEntryPusherService.class);
-		logger = LoggerFactory.getLogger(FlowPusher.class);
-	}
-
-	@Override
-	public void startUp(FloodlightModuleContext context)
-			throws FloodlightModuleException {
-
-		HashMap<String,Object> flow = new HashMap<String,Object>();
-        
-        flow.put(StaticFlowEntryPusher.COLUMN_TP_DST, "80");
-        flow.put(StaticFlowEntryPusher.COLUMN_NW_DST, "10.0.0.2");
-        flow.put(StaticFlowEntryPusher.COLUMN_NW_SRC, "10.0.0.1");
-        flow.put(StaticFlowEntryPusher.COLUMN_PRIORITY, "30001");
-        flow.put(StaticFlowEntryPusher.COLUMN_NAME, "flow1");
-        flow.put(StaticFlowEntryPusher.COLUMN_ACTIVE, Boolean.toString(true));
-        flow.put(StaticFlowEntryPusher.COLUMN_DL_TYPE, "2048");
-        flow.put(StaticFlowEntryPusher.COLUMN_NW_PROTO, "6");
-        flow.put(StaticFlowEntryPusher.COLUMN_SWITCH, "00:00:00:00:00:00:00:01");
-        flow.put(StaticFlowEntryPusher.COLUMN_ACTIONS, "output=controller");
-        
-        storageSource.insertRowAsync(StaticFlowEntryPusher.TABLE_NAME, flow);
-        
-//        flow.put("tp_dst", "80");
-//        flow.put("nw_dst", "10.0.0.3");
-//        flow.put("nw_src", "10.0.0.1");
-//        flow.put("priority", "30001");
-//        flow.put("name", "flow2");
-//        flow.put("active", Boolean.toString(true));
-//        flow.put("dl_type", "2048");
-//        flow.put("nw_proto", "6");
-//        flow.put("switch_id", "00:00:00:00:00:00:00:01");
-//        flow.put("actions", "output=controller");
-//        
-//        storageSource.insertRowAsync("controller_staticflowtableentry", flow);
-        
-//        storageSource.deleteRowAsync("controller_staticflowtableentry", "flow1");
-	}
-
-}
diff --git a/src/main/java/net/floodlightcontroller/accesscontrollist/IACLService.java b/src/main/java/net/floodlightcontroller/accesscontrollist/IACLService.java
index 9ce23238e8ebefe34397cae2deaf9566e841fdbb..50e0c08044ecaa27a23b689359c7897b1c6388dc 100644
--- a/src/main/java/net/floodlightcontroller/accesscontrollist/IACLService.java
+++ b/src/main/java/net/floodlightcontroller/accesscontrollist/IACLService.java
@@ -1,42 +1,56 @@
-/**
- *    Copyright 2015, Big Switch Networks, Inc.
- *    Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China 
- *    Advisers: Keqiu Li and Heng Qi 
- *    This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002) 
- *    and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute.
- *    
- *    Licensed under the Apache License, Version 2.0 (the "License"); you may 
- *    not use this file except in compliance with the License. You may obtain
- *    a copy of the License at
- *    
- *         http://www.apache.org/licenses/LICENSE-2.0 
- *    
- *    Unless required by applicable law or agreed to in writing, software 
- *    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- *    License for the specific language governing permissions and limitations
- *    under the License.
- **/
-
-package net.floodlightcontroller.accesscontrollist;
-
-import java.util.List;
-
-import net.floodlightcontroller.core.module.IFloodlightService;
-
-public interface IACLService extends IFloodlightService {
-	
-    public List<ACLRule> getRules();
-    
-    /**
-     * add a new ACL rule
-     * @param rule
-     * @return whether the rule is added successfully
-     */
-    public boolean addRule(ACLRule rule);
-
-    public void removeRule(int ruleid);
-    
-    public void removeAllRules();
-
-}
+/**
+ *    Copyright 2015, Big Switch Networks, Inc.
+ *    Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China 
+ *    Advisers: Keqiu Li and Heng Qi 
+ *    This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002) 
+ *    and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute.
+ *    
+ *    Licensed under the Apache License, Version 2.0 (the "License"); you may 
+ *    not use this file except in compliance with the License. You may obtain
+ *    a copy of the License at
+ *    
+ *         http://www.apache.org/licenses/LICENSE-2.0 
+ *    
+ *    Unless required by applicable law or agreed to in writing, software 
+ *    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ *    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ *    License for the specific language governing permissions and limitations
+ *    under the License.
+ **/
+
+package net.floodlightcontroller.accesscontrollist;
+
+import java.util.List;
+
+import net.floodlightcontroller.core.module.IFloodlightService;
+
+/**
+ * Service interface exported by ACL module
+ */
+public interface IACLService extends IFloodlightService {
+	
+    /**
+     * Gets a list containing all ACL rules.
+     * @return a list containing all ACL rules
+     */
+    public List<ACLRule> getRules();
+    
+    /**
+     * Add a new ACL rule.
+     * @param rule ACL rule
+     * @return true if successfully added, otherwise false
+     */
+    public boolean addRule(ACLRule rule);
+
+    /**
+     * Removes an existing ACL rule by rule id.
+     * @param ruleId ACL rule identifier
+     */
+    public void removeRule(int ruleid);
+    
+    /**
+     * Clears ACL and resets all.
+     */
+    public void removeAllRules();
+
+}
diff --git a/src/main/java/net/floodlightcontroller/core/IOFSwitch.java b/src/main/java/net/floodlightcontroller/core/IOFSwitch.java
index ccb68176f6086b187071cb4fc9fbf01e9e3df40d..df2c58a743918cc6c27aa89ec7e94dd064b65ed6 100644
--- a/src/main/java/net/floodlightcontroller/core/IOFSwitch.java
+++ b/src/main/java/net/floodlightcontroller/core/IOFSwitch.java
@@ -116,7 +116,13 @@ public interface IOFSwitch extends IOFMessageWriter {
 
     Set<OFCapabilities> getCapabilities();
 
-    short getTables();
+    /**
+     * Get the specific TableIds according to the ofp_table_features.
+     * Not all switches have sequential TableIds, so this will give the
+     * specific TableIds used by the switch.
+     * @return
+     */
+    Collection<TableId> getTables();
 
     /**
      * @return a copy of the description statistics for this switch
@@ -350,5 +356,11 @@ public interface IOFSwitch extends IOFMessageWriter {
      * @return The table features or null if no features are known for the table requested.
      */
     public TableFeatures getTableFeatures(TableId table);
+
+    /**
+     * Get the number of tables as returned by the ofp_features_reply.
+     * @return
+     */
+	short getNumTables();
     
 }
diff --git a/src/main/java/net/floodlightcontroller/core/IOFSwitchBackend.java b/src/main/java/net/floodlightcontroller/core/IOFSwitchBackend.java
index a14ca20199c549213544d85c73a4b7ade72058ed..47edf3a08a1db86de881f516ea607134f9ebe34b 100644
--- a/src/main/java/net/floodlightcontroller/core/IOFSwitchBackend.java
+++ b/src/main/java/net/floodlightcontroller/core/IOFSwitchBackend.java
@@ -27,8 +27,8 @@ import org.projectfloodlight.openflow.protocol.OFMessage;
 import org.projectfloodlight.openflow.protocol.OFPortDesc;
 import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply;
 import org.projectfloodlight.openflow.protocol.OFPortStatus;
-import org.projectfloodlight.openflow.protocol.OFTableFeatures;
 import org.projectfloodlight.openflow.protocol.OFTableFeaturesStatsReply;
+import org.projectfloodlight.openflow.types.TableId;
 
 import net.floodlightcontroller.util.OrderedCollection;
 
@@ -194,4 +194,35 @@ public interface IOFSwitchBackend extends IOFSwitch {
      * @return true if another viable master exists
      */
     boolean hasAnotherMaster();
+    
+    /**
+     * In OF1.3+ switches, the table miss behavior is defined by a flow.
+     * We assume the default behavior is to forward to the controller, but
+     * not all tables need that behavior if a limited set of tables are used.
+     * So, we can cap the number of tables we set this flow in to reduce
+     * clutter in API output and to reduce memory consumption on the switch.
+     * 
+     * This gets the TableId cap set for this particular switch.
+     * 
+     * @return, the highest TableId that should receive a table-miss flow
+     */
+    TableId getMaxTableForTableMissFlow();
+    
+    /**
+     * In OF1.3+ switches, the table miss behavior is defined by a flow.
+     * We assume the default behavior is to forward to the controller, but
+     * not all tables need that behavior if a limited set of tables are used.
+     * So, we can cap the number of tables we set this flow in to reduce
+     * clutter in API output and to reduce memory consumption on the switch.
+     * 
+     * This sets the TableId cap set for this particular switch. If the max
+     * desired is higher than the number of tables this switch supports, the
+     * max table supported will be used:
+     * 
+     * set_max_table = max_supported <= max ? max_supported-1 : max
+     * 
+ 	 * @param max, the highest TableId that should receive a table-miss flow
+     * @return the TableId set as the highest
+     */
+    TableId setMaxTableForTableMissFlow(TableId max);
 }
diff --git a/src/main/java/net/floodlightcontroller/core/OFConnection.java b/src/main/java/net/floodlightcontroller/core/OFConnection.java
index 444e07d7338b1502e32e91f444ae4c1c82ab31f9..36efb215975f38dbe22b41433485c0ba2a49d9b1 100644
--- a/src/main/java/net/floodlightcontroller/core/OFConnection.java
+++ b/src/main/java/net/floodlightcontroller/core/OFConnection.java
@@ -411,6 +411,4 @@ public class OFConnection implements IOFConnection, IOFConnectionBackend{
 		}
 
     }
-
-
 }
diff --git a/src/main/java/net/floodlightcontroller/core/OFSwitch.java b/src/main/java/net/floodlightcontroller/core/OFSwitch.java
index 8b401dad680cdda48eccaa36fad5db0b987d08a3..146bfe19179531501d1f36c66a0404575f70c7ce 100644
--- a/src/main/java/net/floodlightcontroller/core/OFSwitch.java
+++ b/src/main/java/net/floodlightcontroller/core/OFSwitch.java
@@ -97,7 +97,8 @@ public class OFSwitch implements IOFSwitchBackend {
 	protected Set<OFCapabilities> capabilities;
 	protected long buffers;
 	protected Set<OFActionType> actions;
-	protected short tables;
+	protected Collection<TableId> tables;
+	protected short nTables;
 	protected final DatapathId datapathId;
 
 	private Map<TableId, TableFeatures> tableFeaturesByTableId;
@@ -112,8 +113,6 @@ public class OFSwitch implements IOFSwitchBackend {
 	 */
 	private final PortManager portManager;
 
-	//private final TableManager tableManager;
-
 	private volatile boolean connected;
 
 	private volatile OFControllerRole role;
@@ -125,6 +124,8 @@ public class OFSwitch implements IOFSwitchBackend {
 	private SwitchStatus status;
 
 	public static final int OFSWITCH_APP_ID = ident(5);
+	
+	private TableId maxTableToGetTableMissFlow = TableId.of(4); /* this should cover most HW switches that have a couple SW flow tables */
 
 	static {
 		AppCookie.registerApp(OFSwitch.OFSWITCH_APP_ID, "switch");
@@ -164,6 +165,7 @@ public class OFSwitch implements IOFSwitchBackend {
 		this.setAttribute(PROP_SUPPORTS_OFPP_TABLE, Boolean.TRUE);
 
 		this.tableFeaturesByTableId = new HashMap<TableId, TableFeatures>();
+		this.tables = new ArrayList<TableId>();
 	}
 
 	private static int ident(int i) {
@@ -715,6 +717,7 @@ public class OFSwitch implements IOFSwitchBackend {
 		log.trace("Channel: {}, Connected: {}", connections.get(OFAuxId.MAIN).getRemoteInetAddress(), connections.get(OFAuxId.MAIN).isConnected());
 		if (isActive()) {
 			connections.get(OFAuxId.MAIN).write(m);
+			switchManager.handleOutgoingMessage(this, m);
 		} else {
 			log.warn("Attempted to write to switch {} that is SLAVE.", this.getId().toString());
 		}
@@ -746,6 +749,7 @@ public class OFSwitch implements IOFSwitchBackend {
 	public void write(OFMessage m, LogicalOFMessageCategory category) {
 		if (isActive()) {
 			this.getConnection(category).write(m);
+			switchManager.handleOutgoingMessage(this, m);
 		} else {
 			log.warn("Attempted to write to switch {} that is SLAVE.", this.getId().toString());
 		}
@@ -755,6 +759,10 @@ public class OFSwitch implements IOFSwitchBackend {
 	public void write(Iterable<OFMessage> msglist, LogicalOFMessageCategory category) {
 		if (isActive()) {
 			this.getConnection(category).write(msglist);
+			
+			for(OFMessage m : msglist) {
+				switchManager.handleOutgoingMessage(this, m);				
+			}
 		} else {
 			log.warn("Attempted to write to switch {} that is SLAVE.", this.getId().toString());
 		}
@@ -785,6 +793,10 @@ public class OFSwitch implements IOFSwitchBackend {
 	public void write(Iterable<OFMessage> msglist) {
 		if (isActive()) {
 			connections.get(OFAuxId.MAIN).write(msglist);
+						
+			for(OFMessage m : msglist) {
+				switchManager.handleOutgoingMessage(this, m);
+			}
 		} else {
 			log.warn("Attempted to write to switch {} that is SLAVE.", this.getId().toString());
 		}
@@ -816,10 +828,11 @@ public class OFSwitch implements IOFSwitchBackend {
 		this.buffers = featuresReply.getNBuffers();
 
 		if (featuresReply.getVersion().compareTo(OFVersion.OF_13) < 0 ) {
-			// FIXME:LOJI: OF1.3 has per table actions. This needs to be modeled / handled here
+			/* OF1.3+ Per-table actions are set later in the OFTableFeaturesRequest/Reply */
 			this.actions = featuresReply.getActions();
 		}
-		this.tables = featuresReply.getNTables();
+		
+		this.nTables = featuresReply.getNTables();
 	}
 
 	@Override
@@ -869,6 +882,7 @@ public class OFSwitch implements IOFSwitchBackend {
 			List<OFTableFeatures> tfs = reply.getEntries();
 			for (OFTableFeatures tf : tfs) {
 				tableFeaturesByTableId.put(tf.getTableId(), TableFeatures.of(tf));
+				tables.add(tf.getTableId());
 				log.trace("Received TableFeatures for TableId {}, TableName {}", tf.getTableId().toString(), tf.getName());
 			}
 		}
@@ -1075,9 +1089,18 @@ public class OFSwitch implements IOFSwitchBackend {
 	}
 
 
+	/**
+	 * This performs a copy on each 'get'.
+	 * Use sparingly for good performance.
+	 */
+	@Override
+	public Collection<TableId> getTables() {
+		return new ArrayList<TableId>(tables);
+	}
+	
 	@Override
-	public short getTables() {
-		return tables;
+	public short getNumTables() {
+		return this.nTables;
 	}
 
 	@Override
@@ -1208,4 +1231,19 @@ public class OFSwitch implements IOFSwitchBackend {
 	public TableFeatures getTableFeatures(TableId table) {
 		return tableFeaturesByTableId.get(table);
 	}
+
+	@Override
+	public TableId getMaxTableForTableMissFlow() {
+		return maxTableToGetTableMissFlow;
+	}
+	
+	@Override
+	public TableId setMaxTableForTableMissFlow(TableId max) {
+		if (max.getValue() >= nTables) {
+			maxTableToGetTableMissFlow = TableId.of(nTables - 1 < 0 ? 0 : nTables - 1);
+		} else {
+			maxTableToGetTableMissFlow = max;
+		}
+		return maxTableToGetTableMissFlow;
+	}
 }
diff --git a/src/main/java/net/floodlightcontroller/core/SwitchSyncRepresentation.java b/src/main/java/net/floodlightcontroller/core/SwitchSyncRepresentation.java
index ea583510844a32655d7019afe2a10d620a02c892..c537ddd64ecd177c46cbbeca41bd88a6873a5b8b 100644
--- a/src/main/java/net/floodlightcontroller/core/SwitchSyncRepresentation.java
+++ b/src/main/java/net/floodlightcontroller/core/SwitchSyncRepresentation.java
@@ -136,7 +136,7 @@ public class SwitchSyncRepresentation {
     public SwitchSyncRepresentation(IOFSwitch sw) {
         this.dpid = sw.getId();
         this.buffers = sw.getBuffers();
-        this.tables = sw.getTables();
+        this.tables = (short) sw.getNumTables();
         this.capabilities = sw.getCapabilities();
         this.actions = sw.getActions();
         this.ports = toSyncedPortList(sw.getPorts());
diff --git a/src/main/java/net/floodlightcontroller/core/internal/IOFSwitchManager.java b/src/main/java/net/floodlightcontroller/core/internal/IOFSwitchManager.java
index 787acc10949e997f287001ed5344114c73bc0000..e3b0d8ea910f35950a7aa8e4738d1f24d9e637c9 100644
--- a/src/main/java/net/floodlightcontroller/core/internal/IOFSwitchManager.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/IOFSwitchManager.java
@@ -5,6 +5,7 @@ import java.util.List;
 import net.floodlightcontroller.core.FloodlightContext;
 import net.floodlightcontroller.core.IOFConnectionBackend;
 import net.floodlightcontroller.core.IOFSwitch.SwitchStatus;
+import net.floodlightcontroller.core.IOFSwitch;
 import net.floodlightcontroller.core.IOFSwitchBackend;
 import net.floodlightcontroller.core.IOFSwitchDriver;
 import net.floodlightcontroller.core.LogicalOFMessageCategory;
@@ -55,6 +56,13 @@ public interface IOFSwitchManager {
      * @param bContext the Floodlight context of the message, normally null in this case.
      */
     void handleMessage(IOFSwitchBackend sw, OFMessage m, FloodlightContext bContext);
+    
+    /**
+     * Process written messages through the message listeners for the controller
+     * @param sw The switch being written to
+     * @param m the message
+     */
+    public void handleOutgoingMessage(IOFSwitch sw, OFMessage m);
 
     /**
      * Gets an unmodifiable collection of OFSwitchHandshakeHandlers
diff --git a/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java b/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java
index 6b165b5cab61b015996b90c3d9fd5af976e7bc38..a6702b74f6b014a498c41c0f9263e5cba8742eaa 100644
--- a/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java
@@ -2,7 +2,9 @@ package net.floodlightcontroller.core.internal;
 
 import java.io.IOException;
 import java.nio.channels.ClosedChannelException;
+import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Iterator;
 import java.util.List;
 import java.util.concurrent.RejectedExecutionException;
 
@@ -20,6 +22,7 @@ import org.jboss.netty.handler.timeout.IdleStateEvent;
 import org.jboss.netty.handler.timeout.IdleStateHandler;
 import org.jboss.netty.handler.timeout.ReadTimeoutException;
 import org.jboss.netty.util.Timer;
+
 import net.floodlightcontroller.core.IOFConnectionBackend;
 import net.floodlightcontroller.core.OFConnection;
 import net.floodlightcontroller.core.annotations.LogMessageDoc;
@@ -29,6 +32,7 @@ import net.floodlightcontroller.core.internal.OpenflowPipelineFactory.PipelineHa
 import net.floodlightcontroller.core.internal.OpenflowPipelineFactory.PipelineIdleReadTimeout;
 import net.floodlightcontroller.core.internal.OpenflowPipelineFactory.PipelineIdleWriteTimeout;
 import net.floodlightcontroller.debugcounter.IDebugCounterService;
+
 import org.projectfloodlight.openflow.exceptions.OFParseError;
 import org.projectfloodlight.openflow.protocol.OFEchoReply;
 import org.projectfloodlight.openflow.protocol.OFEchoRequest;
@@ -39,9 +43,16 @@ import org.projectfloodlight.openflow.protocol.OFFactory;
 import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
 import org.projectfloodlight.openflow.protocol.OFFeaturesRequest;
 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.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;
 import org.projectfloodlight.openflow.types.OFAuxId;
+import org.projectfloodlight.openflow.types.U32;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -62,10 +73,11 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
 	private Channel channel;
 	private final Timer timer;
 	private volatile OFChannelState state;
-	private OFFactory factory = OFFactories.getFactory(OFVersion.OF_14);
+	private OFFactory factory;
 	private OFFeaturesReply featuresReply;
 	private volatile OFConnection connection;
 	private final IDebugCounterService debugCounters;
+	private final List<U32> ofBitmaps;
 
 	/** transaction Ids to use during handshake. Since only one thread
 	 * calls into the OFChannelHandler we don't need atomic.
@@ -112,6 +124,10 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
 			illegalMessageReceived(m);
 		}
 
+		void processOFPortStatus(OFPortStatus m) {
+			unhandledMessageReceived(m);
+		}
+
 		private final boolean channelHandshakeComplete;
 
 		OFChannelState(boolean handshakeComplete) {
@@ -253,13 +269,16 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
 				case EXPERIMENTER:
 					processOFExperimenter((OFExperimenter)m);
 					break;
-				/* echos can be sent at any time */
+					/* echos can be sent at any time */
 				case ECHO_REPLY:
 					processOFEchoReply((OFEchoReply)m);
 					break;
 				case ECHO_REQUEST:
 					processOFEchoRequest((OFEchoRequest)m);
 					break;
+				case PORT_STATUS:
+					processOFPortStatus((OFPortStatus)m);
+					break;
 				default:
 					illegalMessageReceived(m);
 					break;
@@ -305,14 +324,49 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
 
 		@Override
 		void processOFHello(OFHello m) throws IOException {
-			OFVersion version = m.getVersion();
-			/* Choose the lower of the two supported versions. */
-			if (version.compareTo(factory.getVersion()) < 0) {
-				factory = OFFactories.getFactory(version);
+			OFVersion theirVersion = m.getVersion();
+			OFVersion commonVersion = null;
+			/* First, check if there's a version bitmap supplied. WE WILL ALWAYS HAVE a controller-provided version bitmap. */
+			if (theirVersion.compareTo(OFVersion.OF_13) >= 0 && !m.getElements().isEmpty()) {
+				List<U32> bitmaps = new ArrayList<U32>();
+				List<OFHelloElem> elements = m.getElements();
+				/* Grab all bitmaps supplied */
+				for (OFHelloElem e : elements) {
+					if (m.getVersion().equals(OFVersion.OF_13) 
+							&& e.getType() == OFHelloElemTypeSerializerVer13.VERSIONBITMAP_VAL) {
+						bitmaps.addAll(((OFHelloElemVersionbitmap) e).getBitmaps());
+					} else if (m.getVersion().equals(OFVersion.OF_14) 
+							&& e.getType() == OFHelloElemTypeSerializerVer14.VERSIONBITMAP_VAL) {
+						bitmaps.addAll(((OFHelloElemVersionbitmap) e).getBitmaps());
+					}
+				}
+				/* Lookup highest, common supported OpenFlow version */
+				commonVersion = computeOFVersionFromBitmap(bitmaps);
+				if (commonVersion == null) {
+					log.error("Could not negotiate common OpenFlow version for {} with greatest version bitmap algorithm.", channel.getRemoteAddress());
+					channel.disconnect();
+					return;
+				} else {
+					log.info("Negotiated OpenFlow version of {} for {} with greatest version bitmap algorithm.", commonVersion.toString(), channel.getRemoteAddress());
+					factory = OFFactories.getFactory(commonVersion);
+					OFMessageDecoder decoder = pipeline.get(OFMessageDecoder.class);
+					decoder.setVersion(commonVersion);
+				}
+			}
+			/* If there's not a bitmap present, choose the lower of the two supported versions. */
+			else if (theirVersion.compareTo(factory.getVersion()) < 0) {
+				log.info("Negotiated down to switch OpenFlow version of {} for {} using lesser hello header algorithm.", theirVersion.toString(), channel.getRemoteAddress());
+				factory = OFFactories.getFactory(theirVersion);
+				OFMessageDecoder decoder = pipeline.get(OFMessageDecoder.class);
+				decoder.setVersion(theirVersion);
 			} /* else The controller's version is < or = the switch's, so keep original controller factory. */
-			
-			OFMessageDecoder decoder = pipeline.get(OFMessageDecoder.class);
-			decoder.setVersion(version);
+			else if (theirVersion.equals(factory.getVersion())) {
+				log.info("Negotiated equal OpenFlow version of {} for {} using lesser hello header algorithm.", factory.getVersion().toString(), channel.getRemoteAddress());
+			}
+			else {
+				log.info("Negotiated down to controller OpenFlow version of {} for {} using lesser hello header algorithm.", factory.getVersion().toString(), channel.getRemoteAddress());
+			}
+
 			setState(new WaitFeaturesReplyState());
 		}
 
@@ -341,10 +395,50 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
 			setState(new CompleteState());
 
 		}
+
+		@Override
+		void processOFHello(OFHello m) throws IOException {
+			/*
+			 * Brocade switches send a second hello after
+			 * the controller responds with its hello. This
+			 * might be to confirm the protocol version used,
+			 * but isn't defined in the OF specification.
+			 * 
+			 * We will ignore such hello messages assuming
+			 * the version of the hello is correct according
+			 * to the algorithm in the spec.
+			 * 
+			 * TODO Brocade also sets the XID of this second
+			 * hello as the same XID the controller used.
+			 * Checking for this might help to assure we're
+			 * really dealing with the situation we think
+			 * we are.
+			 */
+			if (m.getVersion().equals(factory.getVersion())) {
+				log.warn("Ignoring second hello from {} in state {}. Might be a Brocade.", channel.getRemoteAddress(), state.toString());
+			} else {
+				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());
+		}
+
 		@Override
 		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);
+			}
+		}
 	};
 
 	/**
@@ -402,7 +496,9 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
 			@Nonnull INewOFConnectionListener newConnectionListener,
 			@Nonnull ChannelPipeline pipeline,
 			@Nonnull IDebugCounterService debugCounters,
-			@Nonnull Timer timer) {
+			@Nonnull Timer timer,
+			@Nonnull List<U32> ofBitmaps,
+			@Nonnull OFFactory defaultFactory) {
 
 		Preconditions.checkNotNull(switchManager, "switchManager");
 		Preconditions.checkNotNull(newConnectionListener, "connectionOpenedListener");
@@ -416,10 +512,45 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
 		this.counters = switchManager.getCounters();
 		this.state = new InitState();
 		this.timer = timer;
+		this.ofBitmaps = ofBitmaps;
+		this.factory = defaultFactory;
 
 		log.debug("constructor on OFChannelHandler {}", String.format("%08x", System.identityHashCode(this)));
 	}
 
+	/**
+	 * Determine the highest supported version of OpenFlow in common
+	 * between both our OFVersion bitmap and the switch's.
+	 * 
+	 * @param theirs, the version bitmaps of the switch
+	 * @return the highest OFVersion in common b/t the two
+	 */
+	private OFVersion computeOFVersionFromBitmap(List<U32> theirs) {		
+		Iterator<U32> theirsItr = theirs.iterator();
+		Iterator<U32> oursItr = ofBitmaps.iterator();
+		OFVersion version = null;
+		int pos = 0;
+		int size = 32;
+		while (theirsItr.hasNext() && oursItr.hasNext()) {
+			int t = theirsItr.next().getRaw();
+			int o = oursItr.next().getRaw();
+
+			int common = t & o; /* Narrow down the results to the common bits */
+			for (int i = 0; i < size; i++) { /* Iterate over and locate the 1's */
+				int tmp = common & (1 << i); /* Select the bit of interest, 0-31 */
+				if (tmp != 0) { /* Is the version of this bit in common? */
+					for (OFVersion v : OFVersion.values()) { /* Which version does this bit represent? */
+						if (v.getWireVersion() == i + (size * pos)) {
+							version = v;
+						}
+					}
+				}
+			}
+			pos++; /* OFVersion position. 1-31 = 1, 32 - 63 = 2, etc. Inc at end so it starts at 0. */
+		}
+		return version;
+	}
+
 	/**
 	 * Determines if the entire switch handshake is complete (channel+switch).
 	 * If the channel handshake is complete the call is forwarded to the
@@ -597,7 +728,7 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
 			log.warn("Could not process message: queue full");
 			counters.rejectedExecutionException.increment();
 		} else if (e.getCause() instanceof IllegalArgumentException) {
-			log.error("Could not decode OpenFlow protocol version from switch {}. Perhaps the switch is trying to use SSL and the controller is not (or vice versa)? {}", getConnectionInfoString(), e.getCause());
+			log.error("Illegal argument exception with switch {}. {}", getConnectionInfoString(), e.getCause());
 			counters.switchSslConfigurationError.increment();
 			ctx.getChannel().close();
 		} else {
@@ -733,11 +864,21 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
 	 */
 	private void sendHelloMessage() throws IOException {
 		// Send initial hello message
-		// FIXME:LOJI: Haven't negotiated version yet, assume 1.3
-		OFHello.Builder builder = factory.buildHello()
-				.setXid(handshakeTransactionIds--);
-		// FIXME: Need to add code here to set the version bitmap hello element
-		OFHello m = builder.build();
+
+		OFHello.Builder builder = factory.buildHello();
+
+		/* Our highest-configured OFVersion does support version bitmaps, so include it */
+		if (factory.getVersion().compareTo(OFVersion.OF_13) >= 0) {
+			List<OFHelloElem> he = new ArrayList<OFHelloElem>();
+			he.add(factory.buildHelloElemVersionbitmap()
+					.setBitmaps(ofBitmaps)
+					.build());
+			builder.setElements(he);
+		}
+
+		OFHello m = builder.setXid(handshakeTransactionIds--)
+				.build();
+		
 		channel.write(Collections.singletonList(m));
 		log.debug("Send hello: {}", m);
 	}
diff --git a/src/main/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandler.java b/src/main/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandler.java
index a17e7d5ae859978d5d7832ae10eed3273d7dc7b2..0e6c15daebc9bb3ba5266359eeae403f01f0af6c 100644
--- a/src/main/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandler.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandler.java
@@ -3,6 +3,7 @@ package net.floodlightcontroller.core.internal;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -24,10 +25,13 @@ import net.floodlightcontroller.core.SwitchDescription;
 import net.floodlightcontroller.core.annotations.LogMessageDoc;
 import net.floodlightcontroller.core.annotations.LogMessageDocs;
 import net.floodlightcontroller.core.internal.OFSwitchAppHandshakePlugin.PluginResultType;
+import net.floodlightcontroller.util.OFDPAUtils;
 
+import org.projectfloodlight.openflow.protocol.OFActionType;
 import org.projectfloodlight.openflow.protocol.OFBadRequestCode;
 import org.projectfloodlight.openflow.protocol.OFBarrierReply;
 import org.projectfloodlight.openflow.protocol.OFBarrierRequest;
+import org.projectfloodlight.openflow.protocol.OFBucket;
 import org.projectfloodlight.openflow.protocol.OFControllerRole;
 import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
 import org.projectfloodlight.openflow.protocol.OFDescStatsRequest;
@@ -44,6 +48,7 @@ import org.projectfloodlight.openflow.protocol.OFFlowModFailedCode;
 import org.projectfloodlight.openflow.protocol.OFFlowRemoved;
 import org.projectfloodlight.openflow.protocol.OFGetConfigReply;
 import org.projectfloodlight.openflow.protocol.OFGetConfigRequest;
+import org.projectfloodlight.openflow.protocol.OFGroupAdd;
 import org.projectfloodlight.openflow.protocol.OFGroupDelete;
 import org.projectfloodlight.openflow.protocol.OFGroupType;
 import org.projectfloodlight.openflow.protocol.OFMessage;
@@ -51,6 +56,7 @@ import org.projectfloodlight.openflow.protocol.OFNiciraControllerRole;
 import org.projectfloodlight.openflow.protocol.OFNiciraControllerRoleReply;
 import org.projectfloodlight.openflow.protocol.OFNiciraControllerRoleRequest;
 import org.projectfloodlight.openflow.protocol.OFPacketIn;
+import org.projectfloodlight.openflow.protocol.OFPortDesc;
 import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply;
 import org.projectfloodlight.openflow.protocol.OFPortStatus;
 import org.projectfloodlight.openflow.protocol.OFQueueGetConfigReply;
@@ -66,14 +72,22 @@ import org.projectfloodlight.openflow.protocol.OFTableFeaturesStatsRequest;
 import org.projectfloodlight.openflow.protocol.OFType;
 import org.projectfloodlight.openflow.protocol.OFVersion;
 import org.projectfloodlight.openflow.protocol.action.OFAction;
+import org.projectfloodlight.openflow.protocol.actionid.OFActionId;
+import org.projectfloodlight.openflow.protocol.actionid.OFActionIdOutput;
 import org.projectfloodlight.openflow.protocol.errormsg.OFBadRequestErrorMsg;
 import org.projectfloodlight.openflow.protocol.errormsg.OFFlowModFailedErrorMsg;
+import org.projectfloodlight.openflow.protocol.instruction.OFInstruction;
+import org.projectfloodlight.openflow.protocol.match.MatchField;
 import org.projectfloodlight.openflow.types.DatapathId;
 import org.projectfloodlight.openflow.types.OFAuxId;
+import org.projectfloodlight.openflow.types.OFBufferId;
 import org.projectfloodlight.openflow.types.OFGroup;
 import org.projectfloodlight.openflow.types.OFPort;
+import org.projectfloodlight.openflow.types.OFVlanVidMatch;
 import org.projectfloodlight.openflow.types.TableId;
+import org.projectfloodlight.openflow.types.U16;
 import org.projectfloodlight.openflow.types.U64;
+import org.projectfloodlight.openflow.types.VlanVid;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -103,7 +117,7 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener {
 	private OFFactory factory = OFFactories.getFactory(OFVersion.OF_14);
 	private final OFFeaturesReply featuresReply;
 	private final Timer timer;
-	
+
 	private volatile OFControllerRole initialRole = null;
 
 	private final ArrayList<OFPortStatus> pendingPortStatusMsg;
@@ -471,15 +485,25 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener {
 			.setGroupType(OFGroupType.SELECT)
 			.build();
 			this.sw.write(delgroup);
+
+			/*
+			 * Make sure we allow these operations to complete before proceeding.
+			 */
+			OFBarrierRequest barrier = factory.buildBarrierRequest()
+					.setXid(handshakeTransactionIds--)
+					.build();
+			sw.write(barrier);
 		}
 	}
 
 	/** 
-	 * Adds an initial table-miss flow to each
-	 * and every table on the switch. This replaces the default behavior of
-	 * forwarding table-miss packets to the controller. The table-miss flows
-	 * inserted will forward all packets that do not match a flow to the 
-	 * controller for processing.
+	 * Adds an initial table-miss flow to tables on the switch. 
+	 * This replaces the default behavior of forwarding table-miss packets 
+	 * to the controller. The table-miss flows inserted will forward all 
+	 * packets that do not match a flow to the controller for processing.
+	 * 
+	 * The OFSwitchManager is checked for used-defined behavior and default
+	 * max table to try to use.
 	 * 
 	 * Adding the default flow only applies to OpenFlow 1.3+ switches, which 
 	 * remove the default forward-to-controller behavior of flow tables.
@@ -498,22 +522,185 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener {
 					.setOutPort(OFPort.CONTROLLER)
 					.build();
 			this.sw.write(deleteFlow);
-						
+
 			ArrayList<OFAction> actions = new ArrayList<OFAction>(1);
 			actions.add(factory.actions().output(OFPort.CONTROLLER, 0xffFFffFF));
 			ArrayList<OFMessage> flows = new ArrayList<OFMessage>();
-			for (int tableId = 0; tableId < this.sw.getTables(); tableId++) {
-				OFFlowAdd defaultFlow = this.factory.buildFlowAdd()
-						.setTableId(TableId.of(tableId))
-						.setPriority(0)
-						.setActions(actions)
-						.build();
-				flows.add(defaultFlow);
+
+			/* If we received a table features reply, iterate over the tables */
+			if (!this.sw.getTables().isEmpty()) {
+				short missCount = 0;
+				for (TableId tid : this.sw.getTables()) {
+					/* Only add the flow if the table exists and if it supports sending to the controller */
+					TableFeatures tf = this.sw.getTableFeatures(tid);
+					if (tf != null && (missCount < this.sw.getMaxTableForTableMissFlow().getValue())) {
+						for (OFActionId aid : tf.getPropApplyActionsMiss().getActionIds()) {
+							if (aid.getType() == OFActionType.OUTPUT) { /* The assumption here is that OUTPUT includes the special port CONTROLLER... */
+								OFFlowAdd defaultFlow = this.factory.buildFlowAdd()
+										.setTableId(tid)
+										.setPriority(0)
+										.setActions(actions)
+										.build();
+								flows.add(defaultFlow);
+								break; /* Stop searching for actions and go to the next table in the list */
+							}
+						}
+					}
+					missCount++;
+				}
+			} else { /* Otherwise, use the number of tables starting at TableId=0 as indicated in the features reply */
+				short missCount = 0;
+				for (short tid = 0; tid < this.sw.getNumTables(); tid++, missCount++) {
+					if (missCount < this.sw.getMaxTableForTableMissFlow().getValue()) { /* Only insert if we want it */
+						OFFlowAdd defaultFlow = this.factory.buildFlowAdd()
+								.setTableId(TableId.of(tid))
+								.setPriority(0)
+								.setActions(actions)
+								.build();
+						flows.add(defaultFlow);
+					}
+				}
 			}
 			this.sw.write(flows);
 		}
 	}
 
+	private void addBroadcomOFDPAFlows() {
+		/*
+		 * By default, we'll assume everyone's on the same VLAN,
+		 * and all switch ports are configured as access ports.
+		 * As such, all packets on the wire will be untagged
+		 * and will only be tagged internally in the switch for 
+		 * pipeline processing.
+		 * 
+		 * If you would like to configure trunks on switches, then
+		 * each switch will need to be configured specifically, as
+		 * we won't be able to automatically handle such a topology.
+		 * 
+		 * Ingress port table (0)     = empty --> default to VLAN table
+		 * VLAN table (10)            = match untagged, apply internal tag, goto termination MAC
+		 * Termination MAC table (20) = match vlan tag and dst MAC, goto bridging table (default miss-->bridging)
+		 * 							  	match only vlan tag, goto controller (DLF or Dest Lookup Failure)
+		 * Bridging table (50)        = default send to policy ACL table
+		 * Policy ACL table (60)      = priority=0 go to controller
+		 *                              write action group of forwarding decision
+		 * Group tables
+		 *   One per interface per VLAN
+		 *   One per VLAN (for flooding)
+		 *   
+		 *  TABLE_INGRESS = 0
+		 *  TABLE_VLAN = 10
+		 *  TABLE_MAC = 20
+		 *  TABLE_UNICAST = 30
+		 *  TABLE_MULTICAST = 40
+		 *  TABLE_BRIDGING = 50
+		 *  TABLE_ACL = 60
+		 */
+
+		/*
+		 * Add flow to match all untagged packets from all ports in VLAN table
+		 */
+		List<OFAction> al = new ArrayList<OFAction>(1);
+		/* al.add(factory.actions().pushVlan(EthType.IPv4)); might not need this */
+		al.add(factory.actions().setVlanVid(VlanVid.ofVlan(1))); /* we'll use 1 internally, just because */
+
+		List<OFInstruction> il = new ArrayList<OFInstruction>(2);
+		il.add(factory.instructions().gotoTable(TableId.of(20))); /* 20 is the termination MAC table */
+		il.add(factory.instructions().applyActions(al));
+		OFFlowAdd fa = factory.buildFlowAdd()
+				.setTableId(TableId.of(10))
+				.setOutPort(OFPort.ANY)
+				.setBufferId(OFBufferId.NO_BUFFER)
+				.setCookie(U64.ZERO)
+				.setMatch(factory.buildMatch()
+						.setExact(MatchField.VLAN_VID, OFVlanVidMatch.UNTAGGED) /* this flow handles untagged */
+						/* do we have to match on the in port here? */
+						.build()
+						)
+						.setInstructions(il)
+						.setPriority(1000)
+						.build();
+		sw.write(fa);
+
+		/*
+		 * The termination MAC flow table must proactively forward to controller specific dst MACs,
+		 * so we need to wait to do wildcarded dst MACs in bridging table upon a miss. Send to bridging
+		 * table by default here.
+		 */
+
+		/*
+		 * Add flow to match all vlan=1 packets to forward to controller in bridging table (DLF).
+		 * Default is to send to policy ACL if this does not match.
+		 */
+		al = new ArrayList<OFAction>(1);
+		al.add(factory.actions().output(OFPort.CONTROLLER, 0xffFFffFF));
+
+		il = new ArrayList<OFInstruction>(1);
+		il.add(factory.instructions().applyActions(al));
+		fa = factory.buildFlowAdd()
+				.setTableId(TableId.of(50))
+				.setOutPort(OFPort.ANY)
+				.setBufferId(OFBufferId.NO_BUFFER)
+				.setCookie(U64.ZERO)
+				.setMatch(factory.buildMatch()
+						.setExact(MatchField.VLAN_VID, OFVlanVidMatch.ofVlan(1)) /* this flow handles recently-tagged VLAN=1 */
+						.build()
+						)
+						.setInstructions(il) 
+						.setPriority(1)
+						.build();
+		sw.write(fa);
+
+		/*
+		 * Lastly, add a group for flooding and for each port.
+		 * This group is only for VLAN=1.
+		 * 
+		 * The flood group has buckets with goto group actions
+		 * for each port's individual L2 group for VLAN=1.
+		 * 
+		 * This means we must first add the individual groups.
+		 */
+		ArrayList<OFBucket> buckets = new ArrayList<OFBucket>();
+		for (OFPortDesc pd : this.sw.getPorts()) {
+			OFPort p = pd.getPortNo();
+			if ((p.getShortPortNumber() & 0xFF00) == 0) { /* TODO Is this correct for special ports? */
+				OFGroupAdd ga = factory.buildGroupAdd()
+						.setGroupType(OFGroupType.INDIRECT)
+						.setBuckets(Collections.singletonList(factory.buildBucket()
+								.setActions(
+										Collections.singletonList((OFAction) factory.actions().buildOutput()
+												.setMaxLen(0xffFFffFF)
+												.setPort(p)
+												.build()))
+												.build()))
+												.setGroup(OFDPAUtils.GroupIds.createL2Interface(p, VlanVid.ofVlan(100)))
+												.build();
+				sw.write(ga);
+
+				/*
+				 * Add the port+bucket for creating the FLOOD group below.
+				 * All L2_INTERFACE groups in a VLAN should be within a
+				 * corresponding L2_FLOOD group of type ALL.
+				 */
+				buckets.add(factory.buildBucket().setActions(
+						Collections.singletonList(
+								(OFAction) factory.actions().buildOutput()
+								.setMaxLen(0xffFFffFF)
+								.setPort(p)
+								.build()
+								)
+						).build());
+			}
+		}
+
+		OFGroupAdd ga = factory.buildGroupAdd()
+				.setGroupType(OFGroupType.ALL)
+				.setBuckets(buckets)
+				.setGroup(OFDPAUtils.GroupIds.createL2Flood(U16.ZERO, VlanVid.ofVlan(100)))
+				.build();
+		sw.write(ga);
+	}
+
 	/**
 	 * Default implementation for message handlers in any state.
 	 *
@@ -766,7 +953,7 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener {
 					switchManager.notifyPortChanged(sw, ev.port, ev.type);
 			}
 		}
-		
+
 		/**
 		 * Handle a table features message.
 		 *
@@ -858,7 +1045,6 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener {
 				}
 				break;
 			default:
-				illegalMessageReceived(m);
 				break;
 			}
 		}
@@ -1014,7 +1200,7 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener {
 			OFDescStatsReply descStatsReply = (OFDescStatsReply) m;
 			SwitchDescription description = new SwitchDescription(descStatsReply);
 			sw = switchManager.getOFSwitchInstance(mainConnection, description, factory, featuresReply.getDatapathId());
-			switchManager.switchAdded(sw);
+			
 			// set switch information
 			// set features reply and channel first so we a DPID and
 			// channel info.
@@ -1022,6 +1208,11 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener {
 			if (portDescStats != null) {
 				sw.setPortDescStats(portDescStats);
 			}
+			/*
+			 * Need to add after setting the features.
+			 */
+			switchManager.switchAdded(sw);
+
 
 			// Handle pending messages now that we have a sw object
 			handlePendingPortStatusMessages(description);
@@ -1042,7 +1233,7 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener {
 			sendHandshakeDescriptionStatsRequest();
 		}
 	}
-	
+
 	/*
 	 * New state: WaitSwitchTableFeaturesReplyState
 	 */
@@ -1053,7 +1244,7 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener {
 			super(false);
 			replies = new ArrayList<OFTableFeaturesStatsReply>();
 		}
-		
+
 		@Override
 		/**
 		 * Accumulate a list of the OFTableFeaturesStatsReply's until there 
@@ -1076,23 +1267,23 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener {
 				/* should only receive TABLE_FEATURES here */
 				log.error("Received {} message but expected TABLE_FEATURES.", m.getStatsType().toString());
 			}
-		
+
 		}
-		
+
 		@Override
 		void processOFError(OFErrorMsg m) {
 			if ((m.getErrType() == OFErrorType.BAD_REQUEST) &&
 					((((OFBadRequestErrorMsg)m).getCode() == OFBadRequestCode.MULTIPART_BUFFER_OVERFLOW)
-					|| ((OFBadRequestErrorMsg)m).getCode() == OFBadRequestCode.BAD_STAT)) { 
+							|| ((OFBadRequestErrorMsg)m).getCode() == OFBadRequestCode.BAD_STAT)) { 
 				log.warn("Switch {} is {} but does not support OFTableFeaturesStats. Assuming all tables can perform any match, action, and instruction in the spec.", 
 						sw.getId().toString(), sw.getOFFactory().getVersion().toString());
 			} else {
 				log.error("Received unexpected OFErrorMsg {} on switch {}.", m.toString(), sw.getId().toString());
 			}
 			nextState();
-			
+
 		}
-		
+
 		private void nextState() {
 			/* move on to the next state */
 			sw.startDriverHandshake();
@@ -1102,7 +1293,7 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener {
 				setState(new WaitSwitchDriverSubHandshakeState());
 			}
 		}
-		
+
 		@Override
 		void enterState() {
 			if (sw.getOFFactory().getVersion().compareTo(OFVersion.OF_13) < 0) {
@@ -1111,7 +1302,7 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener {
 				sendHandshakeTableFeaturesRequest();
 			}
 		}
-		
+
 	}
 
 	public class WaitSwitchDriverSubHandshakeState extends OFSwitchHandshakeState {
@@ -1319,18 +1510,37 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener {
 			super(true);
 		}
 
+		private long sendBarrier() {
+			long xid = handshakeTransactionIds--;
+			OFBarrierRequest barrier = factory.buildBarrierRequest()
+					.setXid(xid)
+					.build();
+			sw.write(barrier); /* don't use ListenableFuture here; we receive via barrier reply OR error (barrier unsupported) */
+			return xid;
+		}
+
 		@Override
 		void enterState() {
-			setSwitchStatus(SwitchStatus.MASTER);
 			if (OFSwitchManager.clearTablesOnEachTransitionToMaster) {
-				log.info("Clearing flow tables of {} on recent transition to MASTER.", sw.getId().toString());
+				log.info("Clearing flow tables of {} on upcoming transition to MASTER.", sw.getId().toString());
 				clearAllTables();
 			} else if (OFSwitchManager.clearTablesOnInitialConnectAsMaster && initialRole == null) { /* don't do it if we were slave first */
 				initialRole = OFControllerRole.ROLE_MASTER;
-				log.info("Clearing flow tables of {} on initial role as MASTER.", sw.getId().toString());
+				log.info("Clearing flow tables of {} on upcoming initial role as MASTER.", sw.getId().toString());
 				clearAllTables();
 			}
+
+			sendBarrier(); /* Need to make sure the tables are clear before adding default flows */
 			addDefaultFlows();
+
+			/*
+			 * We also need a barrier between adding flows and notifying modules of the
+			 * transition to master. Some modules might modify the flow tables and expect 
+			 * the clear/default flow operations above to have completed.
+			 */
+			sendBarrier();
+
+			setSwitchStatus(SwitchStatus.MASTER);
 		}
 
 		@LogMessageDoc(level="WARN",
@@ -1439,7 +1649,7 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener {
 		void processOFFlowRemoved(OFFlowRemoved m) {
 			dispatchMessage(m);
 		}
-		
+
 		@Override
 		void processOFStatsReply(OFStatsReply m) {
 			// TODO Auto-generated method stub
@@ -1762,7 +1972,7 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener {
 				.build();
 		mainConnection.write(descStatsRequest);
 	}
-	
+
 	/**
 	 * send a table features request
 	 */
diff --git a/src/main/java/net/floodlightcontroller/core/internal/OFSwitchManager.java b/src/main/java/net/floodlightcontroller/core/internal/OFSwitchManager.java
index 3c5ea1085267f0e30bec55659d0bdd049e5052ec..bace34438cd4a7c8080e3d36dcbc5dbe48c3fc58 100644
--- a/src/main/java/net/floodlightcontroller/core/internal/OFSwitchManager.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/OFSwitchManager.java
@@ -1,9 +1,13 @@
 package net.floodlightcontroller.core.internal;
 
+import java.io.IOException;
 import java.net.InetSocketAddress;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -51,12 +55,16 @@ import net.floodlightcontroller.debugevent.IEventCategory;
 import net.floodlightcontroller.debugevent.MockDebugEventService;
 
 import org.projectfloodlight.openflow.protocol.OFControllerRole;
+import org.projectfloodlight.openflow.protocol.OFFactories;
 import org.projectfloodlight.openflow.protocol.OFFactory;
 import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
 import org.projectfloodlight.openflow.protocol.OFMessage;
 import org.projectfloodlight.openflow.protocol.OFPortDesc;
+import org.projectfloodlight.openflow.protocol.OFVersion;
 import org.projectfloodlight.openflow.types.DatapathId;
 import org.projectfloodlight.openflow.types.OFAuxId;
+import org.projectfloodlight.openflow.types.TableId;
+import org.projectfloodlight.openflow.types.U32;
 import org.sdnplatform.sync.IStoreClient;
 import org.sdnplatform.sync.IStoreListener;
 import org.sdnplatform.sync.ISyncService;
@@ -65,6 +73,10 @@ import org.sdnplatform.sync.error.SyncException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.databind.MappingJsonFactory;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
@@ -93,14 +105,20 @@ public class OFSwitchManager implements IOFSwitchManager, INewOFConnectionListen
 	private static String keyStorePassword;
 	private static String keyStore;
 	private static boolean useSsl = false;
-	
+
 	protected static boolean clearTablesOnInitialConnectAsMaster = false;
 	protected static boolean clearTablesOnEachTransitionToMaster = false;
 
+	protected static Map<DatapathId, TableId> forwardToControllerFlowsUpToTableByDpid;
+	protected static TableId forwardToControllerFlowsUpToTable = TableId.of(4); /* this should cover most HW switches that have a couple SW-based flow tables */
+
+	protected static List<U32> ofBitmaps;
+	protected static OFFactory defaultFactory;
+	
 	private ConcurrentHashMap<DatapathId, OFSwitchHandshakeHandler> switchHandlers;
 	private ConcurrentHashMap<DatapathId, IOFSwitchBackend> switches;
 	private ConcurrentHashMap<DatapathId, IOFSwitch> syncedSwitches;
-
+	private Set<DatapathId> pastSwitches;
 
 	private ISwitchDriverRegistry driverRegistry;
 
@@ -166,7 +184,17 @@ public class OFSwitchManager implements IOFSwitchManager, INewOFConnectionListen
 			addUpdateToQueue(new SwitchUpdate(dpid, SwitchUpdateType.REMOVED));
 			oldSw.disconnect();
 		}
-
+		
+		/*
+		 * Set some other config options for this switch.
+		 */
+		if (sw.getOFFactory().getVersion().compareTo(OFVersion.OF_13) >= 0) {
+			if (forwardToControllerFlowsUpToTableByDpid.containsKey(sw.getId())) {
+				sw.setMaxTableForTableMissFlow(forwardToControllerFlowsUpToTableByDpid.get(sw.getId()));
+			} else {
+				sw.setMaxTableForTableMissFlow(forwardToControllerFlowsUpToTable);
+			}
+		}
 	}
 
 	@LogMessageDocs({
@@ -476,6 +504,7 @@ public class OFSwitchManager implements IOFSwitchManager, INewOFConnectionListen
 	public IOFSwitchBackend getOFSwitchInstance(IOFConnectionBackend connection,
 			SwitchDescription description,
 			OFFactory factory, DatapathId datapathId) {
+		
 		return this.driverRegistry.getOFSwitchInstance(connection, description, factory, datapathId);
 	}
 
@@ -483,6 +512,11 @@ public class OFSwitchManager implements IOFSwitchManager, INewOFConnectionListen
 	public void handleMessage(IOFSwitchBackend sw, OFMessage m, FloodlightContext bContext) {
 		floodlightProvider.handleMessage(sw, m, bContext);
 	}
+	
+	@Override
+	public void handleOutgoingMessage(IOFSwitch sw, OFMessage m) {
+		floodlightProvider.handleOutgoingMessage(sw, m);
+	}
 
 	@Override
 	public void addOFSwitchDriver(String manufacturerDescriptionPrefix,
@@ -643,6 +677,8 @@ public class OFSwitchManager implements IOFSwitchManager, INewOFConnectionListen
 		driverRegistry = new NaiveSwitchDriverRegistry(this);
 
 		this.switchListeners = new CopyOnWriteArraySet<IOFSwitchListener>();
+		
+		this.pastSwitches = new HashSet<DatapathId>();
 
 		/* TODO @Ryan
 		try {
@@ -679,13 +715,13 @@ public class OFSwitchManager implements IOFSwitchManager, INewOFConnectionListen
 			OFSwitchManager.keyStore = null;
 			OFSwitchManager.keyStorePassword = null;
 		} else {
-			log.warn("SSL enabled. Using secure connections between Floodlight and switches.");
+			log.info("SSL enabled. Using secure connections between Floodlight and switches.");
 			log.info("SSL keystore path: {}, password: {}", path, (pass == null ? "" : pass)); 
 			OFSwitchManager.useSsl = true;
 			OFSwitchManager.keyStore = path;
 			OFSwitchManager.keyStorePassword = (pass == null ? "" : pass);
 		}
-		
+
 		/*
 		 * Get config to define what to do when a switch connects.
 		 * 
@@ -693,33 +729,250 @@ public class OFSwitchManager implements IOFSwitchManager, INewOFConnectionListen
 		 */
 		String clearInitial = configParams.get("clearTablesOnInitialHandshakeAsMaster");
 		String clearLater = configParams.get("clearTablesOnEachTransitionToMaster");
-		
+
 		if (clearInitial == null || clearInitial.isEmpty() || 
 				(!clearInitial.equalsIgnoreCase("yes") && !clearInitial.equalsIgnoreCase("true") &&
-				!clearInitial.equalsIgnoreCase("yep") && !clearInitial.equalsIgnoreCase("ja") &&
-				!clearInitial.equalsIgnoreCase("stimmt"))) {
-			log.warn("Clear switch flow tables on initial handshake as master: FALSE");
+						!clearInitial.equalsIgnoreCase("yep") && !clearInitial.equalsIgnoreCase("ja") &&
+						!clearInitial.equalsIgnoreCase("stimmt"))) {
+			log.info("Clear switch flow tables on initial handshake as master: FALSE");
 			OFSwitchManager.clearTablesOnInitialConnectAsMaster = false;
 		} else {
-			log.warn("Clear switch flow tables on initial handshake as master: TRUE");
+			log.info("Clear switch flow tables on initial handshake as master: TRUE");
 			OFSwitchManager.clearTablesOnInitialConnectAsMaster = true;
 		}
-		
+
 		if (clearLater == null || clearLater.isEmpty() || 
 				(!clearLater.equalsIgnoreCase("yes") && !clearLater.equalsIgnoreCase("true") &&
-				!clearLater.equalsIgnoreCase("yep") && !clearLater.equalsIgnoreCase("ja") &&
-				!clearLater.equalsIgnoreCase("stimmt"))) {
-			log.warn("Clear switch flow tables on each transition to master: FALSE");
+						!clearLater.equalsIgnoreCase("yep") && !clearLater.equalsIgnoreCase("ja") &&
+						!clearLater.equalsIgnoreCase("stimmt"))) {
+			log.info("Clear switch flow tables on each transition to master: FALSE");
 			OFSwitchManager.clearTablesOnEachTransitionToMaster = false;
 		} else {
-			log.warn("Clear switch flow tables on each transition to master: TRUE");
+			log.info("Clear switch flow tables on each transition to master: TRUE");
 			OFSwitchManager.clearTablesOnEachTransitionToMaster = true;
 		}
+
+		/*
+		 * Get default max table for forward to controller flows. 
+		 * Internal default set as class variable at top of OFSwitchManager.
+		 */
+		String defaultFlowsUpToTable = configParams.get("defaultMaxTablesToReceiveTableMissFlow");
+		/* Backward compatibility */
+		if (defaultFlowsUpToTable == null || defaultFlowsUpToTable.isEmpty()) {
+			defaultFlowsUpToTable = configParams.get("defaultMaxTableToReceiveTableMissFlow");
+		}
+		if (defaultFlowsUpToTable != null && !defaultFlowsUpToTable.isEmpty()) {
+			defaultFlowsUpToTable = defaultFlowsUpToTable.toLowerCase().trim();
+			try {
+				forwardToControllerFlowsUpToTable = TableId.of(defaultFlowsUpToTable.startsWith("0x") 
+						? Integer.parseInt(defaultFlowsUpToTable.replaceFirst("0x", ""), 16) 
+								: Integer.parseInt(defaultFlowsUpToTable));
+				log.info("Setting {} as the default max tables to receive table-miss flow", forwardToControllerFlowsUpToTable.toString());
+			} catch (IllegalArgumentException e) {
+				log.error("Invalid table ID {} for default max tables to receive table-miss flow. Using pre-set of {}", 
+						defaultFlowsUpToTable, forwardToControllerFlowsUpToTable.toString());
+			}
+		} else {
+			log.info("Default max tables to receive table-miss flow not configured. Using {}", forwardToControllerFlowsUpToTable.toString());
+		}
+
+		/*
+		 * Get config to define which tables per switch will get a
+		 * default forward-to-controller flow. This can be used to
+		 * reduce the number of such flows if only a reduced set of
+		 * tables are being used.
+		 */
+		String maxPerDpid = configParams.get("maxTablesToReceiveTableMissFlowPerDpid");
+		/* Backward compatibility */
+		if (maxPerDpid == null || maxPerDpid.isEmpty()) {
+			maxPerDpid = configParams.get("maxTableToReceiveTableMissFlowPerDpid");
+		}
+		forwardToControllerFlowsUpToTableByDpid = jsonToSwitchTableIdMap(maxPerDpid);
+	
+		/*
+		 * Get config to determine what versions of OpenFlow we will
+		 * support. The versions will determine the hello's header
+		 * version as well as the OF1.3.1 version bitmap contents.
+		 */
+		String protocols = configParams.get("supportedOpenFlowVersions");
+		Set<OFVersion> ofVersions = new HashSet<OFVersion>();
+		if (protocols != null && !protocols.isEmpty()) {
+			protocols = protocols.toLowerCase();
+			/* 
+			 * Brute-force check for all known versions. 
+			 */
+			if (protocols.contains("1.0") || protocols.contains("10")) {
+				ofVersions.add(OFVersion.OF_10);
+			}
+			if (protocols.contains("1.1") || protocols.contains("11")) {
+				ofVersions.add(OFVersion.OF_11);
+			}
+			if (protocols.contains("1.2") || protocols.contains("12")) {
+				ofVersions.add(OFVersion.OF_12);
+			}
+			if (protocols.contains("1.3") || protocols.contains("13")) {
+				ofVersions.add(OFVersion.OF_13);
+			}
+			if (protocols.contains("1.4") || protocols.contains("14")) {
+				ofVersions.add(OFVersion.OF_14);
+			}
+			/*
+			 * TODO This will need to be updated if/when 
+			 * Loxi is updated to support > 1.4.
+			 * 
+			 * if (protocols.contains("1.5") || protocols.contains("15")) {
+			 *     ofVersions.add(OFVersion.OF_15);
+			 * }
+			 */
+		} else {
+			log.warn("Supported OpenFlow versions not specified. Using Loxi-defined {}", OFVersion.values());
+			ofVersions.addAll(Arrays.asList(OFVersion.values()));
+		}
+		/* Sanity check */
+		if (ofVersions.isEmpty()) {
+			throw new IllegalStateException("OpenFlow version list should never be empty at this point. Make sure it's being populated in OFSwitchManager's init function.");
+		}
+		defaultFactory = computeInitialFactory(ofVersions);
+		ofBitmaps = computeOurVersionBitmaps(ofVersions);
+	}
+	
+	/**
+	 * Find the max version supplied in the supported
+	 * versions list and use it as the default, which
+	 * will subsequently be used in our hello message
+	 * header's version field.
+	 * 
+	 * The factory can be later "downgraded" to a lower
+	 * version depending on what's computed during the
+	 * version-negotiation part of the handshake.
+	 * 
+	 * Assumption: The Set of OFVersion ofVersions
+	 * variable has been set already and is NOT EMPTY.
+	 * 
+	 * @return the highest-version OFFactory we support
+	 */
+	private OFFactory computeInitialFactory(Set<OFVersion> ofVersions) {
+		/* This should NEVER happen. Double-checking. */
+		if (ofVersions == null || ofVersions.isEmpty()) {
+			throw new IllegalStateException("OpenFlow version list should never be null or empty at this point. Make sure it's set in the OFSwitchManager.");
+		}
+		OFVersion highest = null;
+		for (OFVersion v : ofVersions) {
+			if (highest == null) {
+				highest = v;
+			} else if (v.compareTo(highest) > 0) {
+				highest = v;
+			}
+		}
+		/* 
+		 * This assumes highest != null, which
+		 * it won't be if the list of versions
+		 * is not empty.
+		 */
+		return OFFactories.getFactory(highest);
+	}
+	
+	/**
+	 * Based on the list of OFVersions provided as input (or from Loxi),
+	 * create a list of bitmaps for use in version negotiation during a
+	 * cross-version OpenFlow handshake where both parties support 
+	 * OpenFlow versions >= 1.3.1.
+	 * 
+	 * Type Set is used as input to guarantee all unique versions.
+	 * 
+	 * @param ofVersions, the list of bitmaps. Supply to an OFHello message.
+	 * @return list of bitmaps for the versions of OpenFlow we support
+	 */
+	private List<U32> computeOurVersionBitmaps(Set<OFVersion> ofVersions) {
+		/* This should NEVER happen. Double-checking. */
+		if (ofVersions == null || ofVersions.isEmpty()) {
+			throw new IllegalStateException("OpenFlow version list should never be null or empty at this point. Make sure it's set in the OFSwitchManager.");
+		}
 		
-		String tablesToGetFTCFlow = configParams.get("addDefaultSendToControllerFlowInTables");
-		if (tablesToGetFTCFlow == null || tablesToGetFTCFlow.isEmpty()) {
-			
+		int pos = 1; /* initial bitmap in list */
+		int size = 32; /* size of a U32 */
+		int tempBitmap = 0; /* maintain the current bitmap we're working on */
+		List<U32> bitmaps = new ArrayList<U32>();
+		ArrayList<OFVersion> sortedVersions = new ArrayList<OFVersion>(ofVersions);
+		Collections.sort(sortedVersions);
+		for (OFVersion v : sortedVersions) {
+			/* Move on to another bitmap */
+			if (v.getWireVersion() > pos * size - 1 ) {
+				bitmaps.add(U32.ofRaw(tempBitmap));
+				tempBitmap = 0;
+				pos++;
+			}
+			tempBitmap = tempBitmap | (1 << (v.getWireVersion() % size));
+		}
+		if (tempBitmap != 0) {
+			bitmaps.add(U32.ofRaw(tempBitmap));
 		}
+		log.info("Computed OpenFlow version bitmap as {}", Arrays.asList(tempBitmap));
+		return bitmaps;
+	}
+
+	private static Map<DatapathId, TableId> jsonToSwitchTableIdMap(String json) {
+		MappingJsonFactory f = new MappingJsonFactory();
+		JsonParser jp;
+		Map<DatapathId, TableId> retValue = new HashMap<DatapathId, TableId>();
+
+		if (json == null || json.isEmpty()) {
+			return retValue;
+		}
+
+		try {
+			try {
+				jp = f.createJsonParser(json);
+			} catch (JsonParseException e) {
+				throw new IOException(e);
+			}
+
+			jp.nextToken();
+			if (jp.getCurrentToken() != JsonToken.START_OBJECT) {
+				throw new IOException("Expected START_OBJECT");
+			}
+
+			while (jp.nextToken() != JsonToken.END_OBJECT) {
+				if (jp.getCurrentToken() != JsonToken.FIELD_NAME) {
+					throw new IOException("Expected FIELD_NAME");
+				}
+
+				String n = jp.getCurrentName();
+				jp.nextToken();
+				if (jp.getText().equals("")) {
+					continue;
+				}
+
+				DatapathId dpid;
+				try {
+					n = n.trim();
+					dpid = DatapathId.of(n);
+
+					TableId tablesToGetDefaultFlow;
+					String value = jp.getText();
+					if (value != null && !value.isEmpty()) {
+						value = value.trim().toLowerCase();
+						try {
+							tablesToGetDefaultFlow = TableId.of(
+									value.startsWith("0x") 
+									? Integer.parseInt(value.replaceFirst("0x", ""), 16) 
+											: Integer.parseInt(value)
+									); /* will throw exception if outside valid TableId number range */
+							retValue.put(dpid, tablesToGetDefaultFlow);
+							log.info("Setting max tables to receive table-miss flow to {} for DPID {}", 
+									tablesToGetDefaultFlow.toString(), dpid.toString());
+						} catch (IllegalArgumentException e) { /* catches both IllegalArgumentExcpt. and NumberFormatExcpt. */
+							log.error("Invalid value of {} for max tables to receive table-miss flow for DPID {}. Using default of {}.", value, dpid.toString());
+						}
+					}
+				} catch (NumberFormatException e) {
+					log.error("Invalid DPID format {} for max tables to receive table-miss flow for specific DPID. Using default for the intended DPID.", n);
+				}
+			}
+		} catch (IOException e) {
+			log.error("Using default for remaining DPIDs. JSON formatting error in max tables to receive table-miss flow for DPID input String: {}", e);
+		}
+		return retValue;
 	}
 
 	@Override
@@ -757,10 +1010,10 @@ public class OFSwitchManager implements IOFSwitchManager, INewOFConnectionListen
 			bootstrap.setOption("child.keepAlive", true);
 			bootstrap.setOption("child.tcpNoDelay", true);
 			bootstrap.setOption("child.sendBufferSize", Controller.SEND_BUFFER_SIZE);
-
-			ChannelPipelineFactory pfact = useSsl ? new OpenflowPipelineFactory(this, floodlightProvider.getTimer(), this, debugCounterService, keyStore, keyStorePassword) :
-				new OpenflowPipelineFactory(this, floodlightProvider.getTimer(), this, debugCounterService);
-
+			
+			ChannelPipelineFactory pfact = useSsl ? new OpenflowPipelineFactory(this, floodlightProvider.getTimer(), this, debugCounterService, ofBitmaps, defaultFactory, keyStore, keyStorePassword) :
+				new OpenflowPipelineFactory(this, floodlightProvider.getTimer(), this, debugCounterService, ofBitmaps, defaultFactory);
+			
 			bootstrap.setPipelineFactory(pfact);
 			InetSocketAddress sa = new InetSocketAddress(floodlightProvider.getOFPort());
 			final ChannelGroup cg = new DefaultChannelGroup();
diff --git a/src/main/java/net/floodlightcontroller/core/internal/OpenflowPipelineFactory.java b/src/main/java/net/floodlightcontroller/core/internal/OpenflowPipelineFactory.java
index 7dbcbf32eb6869671a4859fed538986016ef1092..11827d02fb50a490c7661179bab38e9b2b46292a 100644
--- a/src/main/java/net/floodlightcontroller/core/internal/OpenflowPipelineFactory.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/OpenflowPipelineFactory.java
@@ -19,6 +19,7 @@ package net.floodlightcontroller.core.internal;
 
 import java.io.FileInputStream;
 import java.security.KeyStore;
+import java.util.List;
 
 import javax.annotation.Nonnull;
 import javax.net.ssl.KeyManager;
@@ -36,6 +37,8 @@ import org.jboss.netty.handler.timeout.IdleStateHandler;
 import org.jboss.netty.handler.timeout.ReadTimeoutHandler;
 import org.jboss.netty.util.ExternalResourceReleasable;
 import org.jboss.netty.util.Timer;
+import org.projectfloodlight.openflow.protocol.OFFactory;
+import org.projectfloodlight.openflow.types.U32;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -56,14 +59,20 @@ implements ChannelPipelineFactory, ExternalResourceReleasable {
 	protected IDebugCounterService debugCounters;
 	private String keyStore;
 	private String keyStorePassword;
+	private OFFactory defaultFactory;
+	private List<U32> ofBitmaps;
 
 	private void init(IOFSwitchManager switchManager, Timer timer,
 			INewOFConnectionListener connectionListener,
-			IDebugCounterService debugCounters) {
+			IDebugCounterService debugCounters,
+			@Nonnull List<U32> ofBitmaps,
+			@Nonnull OFFactory defaultFactory) {
 		this.switchManager = switchManager;
 		this.connectionListener = connectionListener;
 		this.timer = timer;
 		this.debugCounters = debugCounters;
+		this.defaultFactory = defaultFactory;
+		this.ofBitmaps = ofBitmaps;
 		this.idleHandler = new IdleStateHandler(
 				timer,
 				PipelineIdleReadTimeout.MAIN,
@@ -74,9 +83,11 @@ implements ChannelPipelineFactory, ExternalResourceReleasable {
 
 	public OpenflowPipelineFactory(IOFSwitchManager switchManager, Timer timer,
 			INewOFConnectionListener connectionListener,
-			IDebugCounterService debugCounters) {
+			IDebugCounterService debugCounters,
+			@Nonnull List<U32> ofBitmaps,
+			@Nonnull OFFactory defaultFactory) {
 		super();
-		init(switchManager,timer, connectionListener, debugCounters);
+		init(switchManager,timer, connectionListener, debugCounters, ofBitmaps, defaultFactory);
 		this.keyStore = null;
 		this.keyStorePassword = null;
 	}
@@ -84,9 +95,11 @@ implements ChannelPipelineFactory, ExternalResourceReleasable {
 	public OpenflowPipelineFactory(IOFSwitchManager switchManager, Timer timer,
 			INewOFConnectionListener connectionListener,
 			IDebugCounterService debugCounters,
+			@Nonnull List<U32> ofBitmaps,
+			@Nonnull OFFactory defaultFactory,
 			@Nonnull String keyStore, @Nonnull String keyStorePassword) {
 		super();
-		init(switchManager,timer, connectionListener, debugCounters);   
+		init(switchManager,timer, connectionListener, debugCounters, ofBitmaps, defaultFactory);   
 		this.keyStore = keyStore;
 		this.keyStorePassword = keyStorePassword;
 	}
@@ -98,7 +111,9 @@ implements ChannelPipelineFactory, ExternalResourceReleasable {
 				connectionListener,
 				pipeline,
 				debugCounters,
-				timer);
+				timer,
+				ofBitmaps,
+				defaultFactory);
 
 		if (keyStore != null && keyStorePassword != null) {
 			try {
diff --git a/src/main/java/net/floodlightcontroller/core/rest/SwitchRepresentation.java b/src/main/java/net/floodlightcontroller/core/rest/SwitchRepresentation.java
index 03a46f462b68300c61639052b03a7931bfcc4861..7cb96bcee1f84545bcf4069d4aba091503d73cce 100644
--- a/src/main/java/net/floodlightcontroller/core/rest/SwitchRepresentation.java
+++ b/src/main/java/net/floodlightcontroller/core/rest/SwitchRepresentation.java
@@ -47,7 +47,7 @@ public class SwitchRepresentation {
         // IOFSwitch
         this.buffers = sw.getBuffers();
         this.capabilities = sw.getCapabilities();
-        this.tables = sw.getTables();
+        this.tables = sw.getNumTables();
         this.inetAddress = sw.getInetAddress();
         this.sortedPorts = sw.getSortedPorts();
         this.isConnected = sw.isConnected();
diff --git a/src/main/java/net/floodlightcontroller/core/types/MacVlanPair.java b/src/main/java/net/floodlightcontroller/core/types/MacVlanPair.java
index ed7da2dff8d2ee9d33c7121a9a17ca651deca78d..31a5e525a1e0b85ad11b3e608e13a950d929bd86 100644
--- a/src/main/java/net/floodlightcontroller/core/types/MacVlanPair.java
+++ b/src/main/java/net/floodlightcontroller/core/types/MacVlanPair.java
@@ -44,4 +44,8 @@ public class MacVlanPair {
     public int hashCode() {
         return mac.hashCode() ^ vlan.hashCode();
     }
-}
\ No newline at end of file
+
+    public String toString() {
+        return "(" + mac.toString() + ", " + vlan.toString() + ")";
+    }
+}
diff --git a/src/main/java/net/floodlightcontroller/core/web/ControllerRoleResource.java b/src/main/java/net/floodlightcontroller/core/web/ControllerRoleResource.java
index 18b097db1e6fd1332459689375e20e7ffdc81f2a..c64f1a821be38a7c7fa49f1c7f21c51f106e6185 100644
--- a/src/main/java/net/floodlightcontroller/core/web/ControllerRoleResource.java
+++ b/src/main/java/net/floodlightcontroller/core/web/ControllerRoleResource.java
@@ -16,16 +16,13 @@
 
 package net.floodlightcontroller.core.web;
 
-import java.io.IOException;
 import java.util.HashMap;
 import java.util.Map;
 
-import org.restlet.data.Status;
 import org.restlet.resource.ServerResource;
 
 import net.floodlightcontroller.core.IFloodlightProviderService;
 import net.floodlightcontroller.core.RoleInfo;
-import net.floodlightcontroller.core.HARole;
 import net.floodlightcontroller.core.annotations.LogMessageDoc;
 
 import org.restlet.resource.Get;
@@ -34,7 +31,6 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.core.JsonToken;
 import com.fasterxml.jackson.databind.MappingJsonFactory;
 
 public class ControllerRoleResource extends ServerResource {
diff --git a/src/main/java/net/floodlightcontroller/core/web/serializers/IOFSwitchSerializer.java b/src/main/java/net/floodlightcontroller/core/web/serializers/IOFSwitchSerializer.java
index 753685a358b7a5de190694a032327a7b362c216c..d67307676a9d2aa0386f7d4e32d4dee2fd0e644a 100644
--- a/src/main/java/net/floodlightcontroller/core/web/serializers/IOFSwitchSerializer.java
+++ b/src/main/java/net/floodlightcontroller/core/web/serializers/IOFSwitchSerializer.java
@@ -60,7 +60,7 @@ public class IOFSwitchSerializer extends JsonSerializer<IOFSwitch> {
         serializePorts(sw.getPorts(),jGen);
         jGen.writeNumberField("buffers",sw.getBuffers());
         jGen.writeStringField("inetAddress",sw.getInetAddress().toString());
-        jGen.writeNumberField("tables",sw.getTables());
+        jGen.writeNumberField("tables",sw.getNumTables());
         jGen.writeNumberField("connectedSince",sw.getConnectedSince().getTime());
         jGen.writeEndObject();
     }
diff --git a/src/main/java/net/floodlightcontroller/core/web/serializers/IPv6Serializer.java b/src/main/java/net/floodlightcontroller/core/web/serializers/IPv6Serializer.java
new file mode 100644
index 0000000000000000000000000000000000000000..1394f53de55461ceed1836d30daf27f6f9c54332
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/core/web/serializers/IPv6Serializer.java
@@ -0,0 +1,39 @@
+/**
+*    Copyright 2011,2012 Big Switch Networks, Inc. 
+*    Originally created by David Erickson, Stanford University
+* 
+*    Licensed under the Apache License, Version 2.0 (the "License"); you may
+*    not use this file except in compliance with the License. You may obtain
+*    a copy of the License at
+*
+*         http://www.apache.org/licenses/LICENSE-2.0
+*
+*    Unless required by applicable law or agreed to in writing, software
+*    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+*    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+*    License for the specific language governing permissions and limitations
+*    under the License.
+**/
+
+package net.floodlightcontroller.core.web.serializers;
+
+import java.io.IOException;
+
+import org.projectfloodlight.openflow.types.IPv6Address;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+/**
+ * Serialize an IPv6Address in dotted decimal format
+ */
+public class IPv6Serializer extends JsonSerializer<IPv6Address> {
+
+    @Override
+    public void serialize(IPv6Address ipv6, JsonGenerator jGen,
+                          SerializerProvider serializer)
+                                  throws IOException, JsonProcessingException {
+        jGen.writeString(ipv6.toString());
+    }
+}
diff --git a/src/main/java/net/floodlightcontroller/core/web/serializers/OFActionListSerializer.java b/src/main/java/net/floodlightcontroller/core/web/serializers/OFActionListSerializer.java
index 882a625c45cd3e8d4f7e860a42db8bb032a9b086..e0cbf88e0f94e4c6efdefc08c85a7d9ee6ba538f 100644
--- a/src/main/java/net/floodlightcontroller/core/web/serializers/OFActionListSerializer.java
+++ b/src/main/java/net/floodlightcontroller/core/web/serializers/OFActionListSerializer.java
@@ -41,7 +41,6 @@ import org.projectfloodlight.openflow.protocol.oxm.OFOxmEthSrc;
 import org.projectfloodlight.openflow.protocol.oxm.OFOxmEthType;
 import org.projectfloodlight.openflow.protocol.oxm.OFOxmIcmpv4Code;
 import org.projectfloodlight.openflow.protocol.oxm.OFOxmIcmpv4Type;
-
 import org.projectfloodlight.openflow.protocol.oxm.OFOxmIcmpv6Code;
 import org.projectfloodlight.openflow.protocol.oxm.OFOxmIcmpv6Type;
 import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpDscp;
@@ -49,7 +48,6 @@ import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpEcn;
 import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpProto;
 import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpv4Dst;
 import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpv4Src;
-
 import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpv6Dst;
 import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpv6Flabel;
 import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpv6NdSll;
@@ -105,190 +103,199 @@ public class OFActionListSerializer extends JsonSerializer<List<OFAction>> {
      * @throws JsonProcessingException
      */
     public static void serializeActions(JsonGenerator jsonGenerator, List<OFAction> actions) throws IOException, JsonProcessingException {
-        if (actions.isEmpty()) {
-            jsonGenerator.writeStringField("none", "drop");
-        }
+        StringBuilder sb = new StringBuilder();
+        int len = actions.size();
+        int pos = 0;
         for (OFAction a : actions) {
             switch (a.getType()) {
             case OUTPUT:
-                jsonGenerator.writeStringField(ActionUtils.STR_OUTPUT, ((OFActionOutput)a).getPort().toString());
+                sb.append(ActionUtils.STR_OUTPUT).append("=").append(((OFActionOutput)a).getPort().toString());
                 break;
             /* begin OF1.0 ONLY actions */
             case SET_VLAN_VID:
-                jsonGenerator.writeNumberField(ActionUtils.STR_VLAN_SET_VID, ((OFActionSetVlanVid)a).getVlanVid().getVlan());
+                sb.append(ActionUtils.STR_VLAN_SET_VID).append("=").append(((OFActionSetVlanVid)a).getVlanVid().getVlan());
                 break;
             case SET_VLAN_PCP:
-                jsonGenerator.writeNumberField(ActionUtils.STR_VLAN_SET_PCP, ((OFActionSetVlanPcp)a).getVlanPcp().getValue());
+                sb.append(ActionUtils.STR_VLAN_SET_PCP).append("=").append(((OFActionSetVlanPcp)a).getVlanPcp().getValue());
                 break;
             case SET_QUEUE:
-                jsonGenerator.writeNumberField(ActionUtils.STR_QUEUE_SET, ((OFActionSetQueue)a).getQueueId());
+                sb.append(ActionUtils.STR_QUEUE_SET).append("=").append(((OFActionSetQueue)a).getQueueId());
                 break;
             case SET_DL_SRC:
-                jsonGenerator.writeStringField(ActionUtils.STR_DL_SRC_SET, ((OFActionSetDlSrc)a).getDlAddr().toString());
+                sb.append(ActionUtils.STR_DL_SRC_SET).append("=").append(((OFActionSetDlSrc)a).getDlAddr().toString());
                 break;
             case SET_DL_DST:
-                jsonGenerator.writeStringField(ActionUtils.STR_DL_DST_SET, ((OFActionSetDlDst)a).getDlAddr().toString());
+                sb.append(ActionUtils.STR_DL_DST_SET).append("=").append(((OFActionSetDlDst)a).getDlAddr().toString());
                 break;
             case SET_NW_SRC:
-                jsonGenerator.writeStringField(ActionUtils.STR_NW_SRC_SET, ((OFActionSetNwSrc)a).getNwAddr().toString());
+                sb.append(ActionUtils.STR_NW_SRC_SET).append("=").append(((OFActionSetNwSrc)a).getNwAddr().toString());
                 break;
             case SET_NW_DST:
-                jsonGenerator.writeStringField(ActionUtils.STR_NW_DST_SET, ((OFActionSetNwDst)a).getNwAddr().toString());
+                sb.append(ActionUtils.STR_NW_DST_SET).append("=").append(((OFActionSetNwDst)a).getNwAddr().toString());
                 break;
             case SET_NW_TOS:
-                jsonGenerator.writeNumberField(ActionUtils.STR_NW_TOS_SET, ((OFActionSetNwTos)a).getNwTos());
+            	sb.append(ActionUtils.STR_NW_TOS_SET).append("=").append(((OFActionSetNwTos)a).getNwTos());
                 break;    
             case SET_TP_SRC:
-                jsonGenerator.writeNumberField(ActionUtils.STR_TP_SRC_SET, ((OFActionSetTpSrc)a).getTpPort().getPort());
+            	sb.append(ActionUtils.STR_TP_SRC_SET).append("=").append(((OFActionSetTpSrc)a).getTpPort().getPort());
                 break;
             case SET_TP_DST:
-                jsonGenerator.writeNumberField(ActionUtils.STR_TP_DST_SET, ((OFActionSetTpDst)a).getTpPort().getPort());
+            	sb.append(ActionUtils.STR_TP_DST_SET).append("=").append(((OFActionSetTpDst)a).getTpPort().getPort());
                 break;
             /* end OF1.0 ONLY actions; begin OF1.1+ actions */
             case ENQUEUE:
-                jsonGenerator.writeNumberField(ActionUtils.STR_ENQUEUE, ((OFActionEnqueue)a).getPort().getPortNumber());
+                sb.append(ActionUtils.STR_ENQUEUE).append("=").append(((OFActionEnqueue)a).getPort().getPortNumber());
                 break;
             case GROUP:
-                jsonGenerator.writeStringField(ActionUtils.STR_GROUP, ((OFActionGroup)a).getGroup().toString());
+            	sb.append(ActionUtils.STR_GROUP).append("=").append(((OFActionGroup)a).getGroup().toString());
                 break;
             case STRIP_VLAN:
-                jsonGenerator.writeStringField(ActionUtils.STR_VLAN_STRIP, ActionUtils.STR_NOT_APPLICABLE);
+            	sb.append(ActionUtils.STR_VLAN_STRIP).append("=").append(ActionUtils.STR_NOT_APPLICABLE);
                 break;
             case PUSH_VLAN:
-                jsonGenerator.writeNumberField(ActionUtils.STR_VLAN_PUSH, ((OFActionPushVlan)a).getEthertype().getValue());
+            	sb.append(ActionUtils.STR_VLAN_PUSH).append("=").append(((OFActionPushVlan)a).getEthertype().getValue());
                 break;
             case PUSH_MPLS:
-                jsonGenerator.writeNumberField(ActionUtils.STR_MPLS_PUSH, ((OFActionPushMpls)a).getEthertype().getValue());
+            	sb.append(ActionUtils.STR_MPLS_PUSH).append("=").append(((OFActionPushMpls)a).getEthertype().getValue());
                 break;
             case PUSH_PBB:
-                jsonGenerator.writeNumberField(ActionUtils.STR_PBB_PUSH, ((OFActionPushPbb)a).getEthertype().getValue());
+            	sb.append(ActionUtils.STR_PBB_PUSH).append("=").append(((OFActionPushPbb)a).getEthertype().getValue());
                 break;
             case POP_VLAN:
-                jsonGenerator.writeStringField(ActionUtils.STR_VLAN_POP, ActionUtils.STR_NOT_APPLICABLE);
+            	sb.append(ActionUtils.STR_VLAN_POP).append("=").append(ActionUtils.STR_NOT_APPLICABLE);
                 break;
             case POP_MPLS:
-                jsonGenerator.writeNumberField(ActionUtils.STR_MPLS_POP, ((OFActionPopMpls)a).getEthertype().getValue());
+            	sb.append(ActionUtils.STR_MPLS_POP).append("=").append(((OFActionPopMpls)a).getEthertype().getValue());
                 break;
             case POP_PBB:
-                jsonGenerator.writeStringField(ActionUtils.STR_PBB_POP, ActionUtils.STR_NOT_APPLICABLE);
+            	sb.append(ActionUtils.STR_PBB_POP).append("=").append(ActionUtils.STR_NOT_APPLICABLE);
                 break;
             case COPY_TTL_IN:
-                jsonGenerator.writeStringField(ActionUtils.STR_TTL_IN_COPY, ActionUtils.STR_NOT_APPLICABLE);
+            	sb.append(ActionUtils.STR_TTL_IN_COPY).append("=").append(ActionUtils.STR_NOT_APPLICABLE);
                 break;
             case COPY_TTL_OUT:
-                jsonGenerator.writeStringField(ActionUtils.STR_TTL_OUT_COPY, ActionUtils.STR_NOT_APPLICABLE);
+            	sb.append(ActionUtils.STR_TTL_OUT_COPY).append("=").append(ActionUtils.STR_NOT_APPLICABLE);
                 break;
             case DEC_NW_TTL:
-                jsonGenerator.writeStringField(ActionUtils.STR_NW_TTL_DEC, ActionUtils.STR_NOT_APPLICABLE);
+            	sb.append(ActionUtils.STR_NW_TTL_DEC).append("=").append(ActionUtils.STR_NOT_APPLICABLE);
                 break;
             case DEC_MPLS_TTL:
-                jsonGenerator.writeStringField(ActionUtils.STR_MPLS_TTL_DEC, ActionUtils.STR_NOT_APPLICABLE);
+            	sb.append(ActionUtils.STR_MPLS_TTL_DEC).append("=").append(ActionUtils.STR_NOT_APPLICABLE);
                 break;
             case SET_MPLS_LABEL:
-                jsonGenerator.writeNumberField(ActionUtils.STR_MPLS_LABEL_SET, ((OFActionSetMplsLabel)a).getMplsLabel());
+            	sb.append(ActionUtils.STR_MPLS_LABEL_SET).append("=").append(((OFActionSetMplsLabel)a).getMplsLabel());
                 break;
             case SET_MPLS_TC:
-                jsonGenerator.writeNumberField(ActionUtils.STR_MPLS_TC_SET, ((OFActionSetMplsTc)a).getMplsTc());
+            	sb.append(ActionUtils.STR_MPLS_TC_SET).append("=").append(((OFActionSetMplsTc)a).getMplsTc());
                 break;
             case SET_MPLS_TTL:
-                jsonGenerator.writeNumberField(ActionUtils.STR_MPLS_TTL_SET, ((OFActionSetMplsTtl)a).getMplsTtl());
+                sb.append(ActionUtils.STR_MPLS_TTL_SET).append("=").append(((OFActionSetMplsTtl)a).getMplsTtl());
                 break;
             case SET_NW_ECN:
-                jsonGenerator.writeNumberField(ActionUtils.STR_NW_ECN_SET, ((OFActionSetNwEcn)a).getNwEcn().getEcnValue());
+            	sb.append(ActionUtils.STR_NW_ECN_SET).append("=").append(((OFActionSetNwEcn)a).getNwEcn().getEcnValue());
                 break;
             case SET_NW_TTL:
-                jsonGenerator.writeNumberField(ActionUtils.STR_NW_TTL_SET, ((OFActionSetNwTtl)a).getNwTtl());
+            	sb.append(ActionUtils.STR_NW_TTL_SET).append("=").append(((OFActionSetNwTtl)a).getNwTtl());
                 break;
             case EXPERIMENTER:
-                jsonGenerator.writeNumberField(ActionUtils.STR_EXPERIMENTER, ((OFActionExperimenter)a).getExperimenter());
+            	sb.append(ActionUtils.STR_EXPERIMENTER).append("=").append(((OFActionExperimenter)a).getExperimenter());
                 break;
             case SET_FIELD:
                 if (((OFActionSetField)a).getField() instanceof OFOxmArpOp) {
-                    jsonGenerator.writeNumberField(MatchUtils.STR_ARP_OPCODE, ((OFOxmArpOp) ((OFActionSetField) a).getField()).getValue().getOpcode());
+                	sb.append(MatchUtils.STR_ARP_OPCODE).append("=").append(((OFOxmArpOp) ((OFActionSetField) a).getField()).getValue().getOpcode());
                 } else if (((OFActionSetField)a).getField() instanceof OFOxmArpSha) {
-                    jsonGenerator.writeStringField(MatchUtils.STR_ARP_SHA, ((OFOxmArpSha) ((OFActionSetField) a).getField()).getValue().toString()); // macaddress formats string already
+                	sb.append(MatchUtils.STR_ARP_SHA).append("=").append(((OFOxmArpSha) ((OFActionSetField) a).getField()).getValue().toString()); // macaddress formats string already
                 } else if (((OFActionSetField)a).getField() instanceof OFOxmArpTha) {
-                    jsonGenerator.writeStringField(MatchUtils.STR_ARP_DHA, ((OFOxmArpTha) ((OFActionSetField) a).getField()).getValue().toString());
+                	sb.append(MatchUtils.STR_ARP_DHA).append("=").append(((OFOxmArpTha) ((OFActionSetField) a).getField()).getValue().toString());
                 } else if (((OFActionSetField)a).getField() instanceof OFOxmArpSpa) {
-                    jsonGenerator.writeStringField(MatchUtils.STR_ARP_SPA, ((OFOxmArpSpa) ((OFActionSetField) a).getField()).getValue().toString()); // ipaddress formats string already
+                	sb.append(MatchUtils.STR_ARP_SPA).append("=").append(((OFOxmArpSpa) ((OFActionSetField) a).getField()).getValue().toString()); // ipaddress formats string already
                 } else if (((OFActionSetField)a).getField() instanceof OFOxmArpTpa) {
-                    jsonGenerator.writeStringField(MatchUtils.STR_ARP_DPA, ((OFOxmArpTpa) ((OFActionSetField) a).getField()).getValue().toString()); 
+                	sb.append(MatchUtils.STR_ARP_DPA).append("=").append(((OFOxmArpTpa) ((OFActionSetField) a).getField()).getValue().toString()); 
                 } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6NdSll) {                		
-                	jsonGenerator.writeStringField(MatchUtils.STR_IPV6_ND_SSL, ((OFOxmIpv6NdSll) ((OFActionSetField) a).getField()).getValue().toString());
+                	sb.append(MatchUtils.STR_IPV6_ND_SSL).append("=").append(((OFOxmIpv6NdSll) ((OFActionSetField) a).getField()).getValue().toString());
             	} else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6NdTll) {                		
-            		jsonGenerator.writeStringField(MatchUtils.STR_IPV6_ND_TTL, ((OFOxmIpv6NdTll) ((OFActionSetField) a).getField()).getValue().toString());
+            		sb.append(MatchUtils.STR_IPV6_ND_TTL).append("=").append(((OFOxmIpv6NdTll) ((OFActionSetField) a).getField()).getValue().toString());
             	} else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6NdTarget) {                		
-            		jsonGenerator.writeStringField(MatchUtils.STR_IPV6_ND_TARGET, ((OFOxmIpv6NdTarget) ((OFActionSetField) a).getField()).getValue().toString()); 
+            		sb.append(MatchUtils.STR_IPV6_ND_TARGET).append("=").append(((OFOxmIpv6NdTarget) ((OFActionSetField) a).getField()).getValue().toString()); 
             	}
                 /* DATA LAYER */
                 else if (((OFActionSetField)a).getField() instanceof OFOxmEthType) {
-                    jsonGenerator.writeNumberField(MatchUtils.STR_DL_TYPE, ((OFOxmEthType) ((OFActionSetField) a).getField()).getValue().getValue());
+                	sb.append(MatchUtils.STR_DL_TYPE).append("=").append(((OFOxmEthType) ((OFActionSetField) a).getField()).getValue().getValue());
                 } else if (((OFActionSetField)a).getField() instanceof OFOxmEthSrc) {
-                    jsonGenerator.writeStringField(MatchUtils.STR_DL_SRC, ((OFOxmEthSrc) ((OFActionSetField) a).getField()).getValue().toString());
+                	sb.append(MatchUtils.STR_DL_SRC).append("=").append(((OFOxmEthSrc) ((OFActionSetField) a).getField()).getValue().toString());
                 } else if (((OFActionSetField)a).getField() instanceof OFOxmEthDst) {
-                    jsonGenerator.writeStringField(MatchUtils.STR_DL_DST, ((OFOxmEthDst) ((OFActionSetField) a).getField()).getValue().toString()); 
+                	sb.append(MatchUtils.STR_DL_DST).append("=").append(((OFOxmEthDst) ((OFActionSetField) a).getField()).getValue().toString()); 
                 } else if (((OFActionSetField)a).getField() instanceof OFOxmVlanVid) {
-                    jsonGenerator.writeNumberField(MatchUtils.STR_DL_VLAN, ((OFOxmVlanVid) ((OFActionSetField) a).getField()).getValue().getVlan()); 
+                	sb.append(MatchUtils.STR_DL_VLAN).append("=").append(((OFOxmVlanVid) ((OFActionSetField) a).getField()).getValue().getVlan()); 
                 } else if (((OFActionSetField)a).getField() instanceof OFOxmVlanPcp) {
                 } 
                 /* ICMP */
                 else if (((OFActionSetField)a).getField() instanceof OFOxmIcmpv4Code) {
-                    jsonGenerator.writeNumberField(MatchUtils.STR_ICMP_CODE, ((OFOxmIcmpv4Code) ((OFActionSetField) a).getField()).getValue().getCode()); 
+                	sb.append(MatchUtils.STR_ICMP_CODE).append("=").append(((OFOxmIcmpv4Code) ((OFActionSetField) a).getField()).getValue().getCode()); 
                 } else if (((OFActionSetField)a).getField() instanceof OFOxmIcmpv4Type) {
-                    jsonGenerator.writeNumberField(MatchUtils.STR_ICMP_TYPE, ((OFOxmIcmpv4Type) ((OFActionSetField) a).getField()).getValue().getType()); 
+                	sb.append(MatchUtils.STR_ICMP_TYPE).append("=").append(((OFOxmIcmpv4Type) ((OFActionSetField) a).getField()).getValue().getType()); 
                 } else if (((OFActionSetField)a).getField() instanceof OFOxmIcmpv6Code) {                		
-                	jsonGenerator.writeNumberField(MatchUtils.STR_ICMPV6_CODE, ((OFOxmIcmpv6Code) ((OFActionSetField) a).getField()).getValue().getRaw()); 
+                	sb.append(MatchUtils.STR_ICMPV6_CODE).append("=").append(((OFOxmIcmpv6Code) ((OFActionSetField) a).getField()).getValue().getRaw()); 
             	}  else if (((OFActionSetField)a).getField() instanceof OFOxmIcmpv6Type) {                		
-            		jsonGenerator.writeNumberField(MatchUtils.STR_ICMPV6_TYPE, ((OFOxmIcmpv6Type) ((OFActionSetField) a).getField()).getValue().getRaw()); 
+            		sb.append(MatchUtils.STR_ICMPV6_TYPE).append("=").append(((OFOxmIcmpv6Type) ((OFActionSetField) a).getField()).getValue().getRaw()); 
             	}
                 /* NETWORK LAYER */
                 else if (((OFActionSetField)a).getField() instanceof OFOxmIpProto) {
-                    jsonGenerator.writeNumberField(MatchUtils.STR_NW_PROTO, ((OFOxmIpProto) ((OFActionSetField) a).getField()).getValue().getIpProtocolNumber()); 
+                	sb.append(MatchUtils.STR_NW_PROTO).append("=").append(((OFOxmIpProto) ((OFActionSetField) a).getField()).getValue().getIpProtocolNumber()); 
                 } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv4Src) {
-                    jsonGenerator.writeStringField(MatchUtils.STR_NW_SRC, ((OFOxmIpv4Src) ((OFActionSetField) a).getField()).getValue().toString()); 
+                	sb.append(MatchUtils.STR_NW_SRC).append("=").append(((OFOxmIpv4Src) ((OFActionSetField) a).getField()).getValue().toString()); 
                 } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv4Dst) {
-                    jsonGenerator.writeStringField(MatchUtils.STR_NW_DST, ((OFOxmIpv4Dst) ((OFActionSetField) a).getField()).getValue().toString()); 
+                	sb.append(MatchUtils.STR_NW_DST).append("=").append(((OFOxmIpv4Dst) ((OFActionSetField) a).getField()).getValue().toString()); 
                 } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6Src) {                		
-                	jsonGenerator.writeStringField(MatchUtils.STR_IPV6_SRC, ((OFOxmIpv6Src) ((OFActionSetField) a).getField()).getValue().toString()); 
+                	sb.append(MatchUtils.STR_IPV6_SRC).append("=").append(((OFOxmIpv6Src) ((OFActionSetField) a).getField()).getValue().toString()); 
             	} else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6Dst) {                		
-            		jsonGenerator.writeStringField(MatchUtils.STR_IPV6_DST, ((OFOxmIpv6Dst) ((OFActionSetField) a).getField()).getValue().toString()); 
+            		sb.append(MatchUtils.STR_IPV6_DST).append("=").append(((OFOxmIpv6Dst) ((OFActionSetField) a).getField()).getValue().toString()); 
             	} else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6Flabel) {                		
-            		jsonGenerator.writeStringField(MatchUtils.STR_IPV6_FLOW_LABEL, ((OFOxmIpv6Flabel) ((OFActionSetField) a).getField()).getValue().toString()); 
+            		sb.append(MatchUtils.STR_IPV6_FLOW_LABEL).append("=").append(((OFOxmIpv6Flabel) ((OFActionSetField) a).getField()).getValue().toString()); 
             	} else if (((OFActionSetField)a).getField() instanceof OFOxmIpEcn) {
-                    jsonGenerator.writeNumberField(MatchUtils.STR_NW_ECN, ((OFOxmIpEcn) ((OFActionSetField) a).getField()).getValue().getEcnValue()); 
+            		sb.append(MatchUtils.STR_NW_ECN).append("=").append(((OFOxmIpEcn) ((OFActionSetField) a).getField()).getValue().getEcnValue()); 
                 } else if (((OFActionSetField)a).getField() instanceof OFOxmIpDscp) {
-                    jsonGenerator.writeNumberField(MatchUtils.STR_NW_DSCP, ((OFOxmIpDscp) ((OFActionSetField) a).getField()).getValue().getDscpValue()); 
+                	sb.append(MatchUtils.STR_NW_DSCP).append("=").append(((OFOxmIpDscp) ((OFActionSetField) a).getField()).getValue().getDscpValue()); 
                 } 
                 /* TRANSPORT LAYER, TCP, UDP, and SCTP */
                 else if (((OFActionSetField)a).getField() instanceof OFOxmTcpSrc) {
-                    jsonGenerator.writeNumberField(MatchUtils.STR_TCP_SRC, ((OFOxmTcpSrc) ((OFActionSetField) a).getField()).getValue().getPort()); 
+                	sb.append(MatchUtils.STR_TCP_SRC).append("=").append(((OFOxmTcpSrc) ((OFActionSetField) a).getField()).getValue().getPort()); 
                 } else if (((OFActionSetField)a).getField() instanceof OFOxmTcpDst) {
-                    jsonGenerator.writeNumberField(MatchUtils.STR_TCP_DST, ((OFOxmTcpDst) ((OFActionSetField) a).getField()).getValue().getPort()); 
+                	sb.append(MatchUtils.STR_TCP_DST).append("=").append(((OFOxmTcpDst) ((OFActionSetField) a).getField()).getValue().getPort()); 
                 } else if (((OFActionSetField)a).getField() instanceof OFOxmUdpSrc) {
-                    jsonGenerator.writeNumberField(MatchUtils.STR_UDP_SRC, ((OFOxmUdpSrc) ((OFActionSetField) a).getField()).getValue().getPort()); 
+                	sb.append(MatchUtils.STR_UDP_SRC).append("=").append(((OFOxmUdpSrc) ((OFActionSetField) a).getField()).getValue().getPort()); 
                 } else if (((OFActionSetField)a).getField() instanceof OFOxmUdpDst) {
-                    jsonGenerator.writeNumberField(MatchUtils.STR_UDP_DST, ((OFOxmUdpDst) ((OFActionSetField) a).getField()).getValue().getPort()); 
+                	sb.append(MatchUtils.STR_UDP_DST).append("=").append(((OFOxmUdpDst) ((OFActionSetField) a).getField()).getValue().getPort()); 
                 } else if (((OFActionSetField)a).getField() instanceof OFOxmSctpSrc) {
-                    jsonGenerator.writeNumberField(MatchUtils.STR_SCTP_SRC, ((OFOxmSctpSrc) ((OFActionSetField) a).getField()).getValue().getPort()); 
+                	sb.append(MatchUtils.STR_SCTP_SRC).append("=").append(((OFOxmSctpSrc) ((OFActionSetField) a).getField()).getValue().getPort()); 
                 } else if (((OFActionSetField)a).getField() instanceof OFOxmSctpDst) {
-                    jsonGenerator.writeNumberField(MatchUtils.STR_SCTP_DST, ((OFOxmSctpDst) ((OFActionSetField) a).getField()).getValue().getPort()); 
+                	sb.append(MatchUtils.STR_SCTP_DST).append("=").append(((OFOxmSctpDst) ((OFActionSetField) a).getField()).getValue().getPort()); 
                 }
                 /* MPLS */
                 else if (((OFActionSetField)a).getField() instanceof OFOxmMplsLabel) {
-                    jsonGenerator.writeNumberField(MatchUtils.STR_MPLS_LABEL, ((OFOxmMplsLabel) ((OFActionSetField) a).getField()).getValue().getValue()); 
+                	sb.append(MatchUtils.STR_MPLS_LABEL).append("=").append(((OFOxmMplsLabel) ((OFActionSetField) a).getField()).getValue().getValue()); 
                 } else if (((OFActionSetField)a).getField() instanceof OFOxmMplsTc) {
-                    jsonGenerator.writeNumberField(MatchUtils.STR_MPLS_TC, ((OFOxmMplsTc) ((OFActionSetField) a).getField()).getValue().getValue()); 
+                	sb.append(MatchUtils.STR_MPLS_TC).append("=").append(((OFOxmMplsTc) ((OFActionSetField) a).getField()).getValue().getValue()); 
                 } else if (((OFActionSetField)a).getField() instanceof OFOxmMplsBos) {
-                    jsonGenerator.writeStringField(MatchUtils.STR_MPLS_TC, ((OFOxmMplsBos) ((OFActionSetField) a).getField()).getValue().toString()); 
+                	sb.append(MatchUtils.STR_MPLS_TC).append("=").append(((OFOxmMplsBos) ((OFActionSetField) a).getField()).getValue().toString()); 
                 } 
                 /* METADATA */
                 else if (((OFActionSetField)a).getField() instanceof OFOxmMetadata) {
-                    jsonGenerator.writeNumberField(MatchUtils.STR_METADATA, ((OFOxmMetadata) ((OFActionSetField) a).getField()).getValue().getValue().getValue()); 
+                	sb.append(MatchUtils.STR_METADATA).append("=").append(((OFOxmMetadata) ((OFActionSetField) a).getField()).getValue().getValue().getValue()); 
                 } else {
                     logger.error("Could not decode Set-Field action field: {}", ((OFActionSetField) a));
-                    // need to get a logger in here somehow log.error("Could not decode Set-Field action field: {}", ((OFActionSetField) a));
                 }
             } // end switch over action type
+            pos++;
+            if (pos < len) {
+            	sb.append(",");
+            }
         } // end for over all actions
+        
+        if (actions.isEmpty()) {
+            jsonGenerator.writeStringField("none", "drop");
+        } else {
+        	jsonGenerator.writeStringField("actions", sb.toString());
+        }
     } // end method
 }
diff --git a/src/main/java/net/floodlightcontroller/core/web/serializers/OXMSerializer.java b/src/main/java/net/floodlightcontroller/core/web/serializers/OXMSerializer.java
new file mode 100644
index 0000000000000000000000000000000000000000..c0b13916edfe037ab43e298f68be93fe18279967
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/core/web/serializers/OXMSerializer.java
@@ -0,0 +1,440 @@
+package net.floodlightcontroller.core.web.serializers;
+
+import net.floodlightcontroller.util.MatchUtils;
+
+import org.projectfloodlight.openflow.protocol.ver14.OFOxmClassSerializerVer14;
+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.IPv6Address;
+import org.projectfloodlight.openflow.types.IPv6FlowLabel;
+import org.projectfloodlight.openflow.types.IpDscp;
+import org.projectfloodlight.openflow.types.IpEcn;
+import org.projectfloodlight.openflow.types.IpProtocol;
+import org.projectfloodlight.openflow.types.MacAddress;
+import org.projectfloodlight.openflow.types.OFBooleanValue;
+import org.projectfloodlight.openflow.types.OFMetadata;
+import org.projectfloodlight.openflow.types.OFPort;
+import org.projectfloodlight.openflow.types.TransportPort;
+import org.projectfloodlight.openflow.types.U32;
+import org.projectfloodlight.openflow.types.U64;
+import org.projectfloodlight.openflow.types.U8;
+import org.projectfloodlight.openflow.types.VlanPcp;
+import org.projectfloodlight.openflow.types.VlanVid;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+
+/**
+ * A handy class to convert OXMs (and NXMs) to and from
+ * their IDs (U32=[class]+[field]+[has_mask]+[length])
+ * and their string representations. The strings are
+ * defined in {@link net.floodlightcontroller.util.MatchUtils},
+ * with the exception of some unique NXMs and OpenFlow 1.5 OXMs 
+ * not technically supported by Loxi/Floodlight yet. Any NXM
+ * string is prefixed with an "nxm_" followed by the string
+ * used for the corresponding OXM. For example, an ingress port
+ * OXM is "in_port", while the NXM version is "nxm_in_port".
+ * 
+ * Any unique NXM without an OXM counterpart is assigned a string
+ * to inform of the NXM. For example, NXM 33, "nxm_pkt_mark", marks
+ * or matches on marked packets as Linux IP tables can do. This is
+ * not supported in the OpenFlow spec, thus the NXM is assigned the
+ * string "nxm_pkt_mark" within this class and does not have a string
+ * predefined in {@link net.floodlightcontroller.util.MatchUtils}.
+ * 
+ * Any OXM with the HAS_MASKED bit set will have it's string
+ * representation appended with an "_masked" or the string defined in
+ * the STR_MASKED class variable below.
+ * 
+ * As an implementation note, a Google BiMap is used as the storage
+ * mechanism. This allows fast lookup in both directions from U32 to 
+ * String or from String back to U32. Note that unique keys AND values
+ * are required in a BiMap.
+ * 
+ * @author Ryan Izard, rizard@g.clemson.edu, ryan.izard@bigswitch.com
+ *
+ */
+public class OXMSerializer {	
+	
+	/*
+	 * OXM IDs for OpenFlow versions 1.5 and below are the same, 
+	 * where each version simply adds to the already populated
+	 * list of OXM IDs. (IDs don't change b/t OF versions.)
+	 * As such, we'll assume OF1.4 here, which includes 1.3.
+	 * 
+	 * TODO Later, when Loxi supports 1.5, we'll change to
+	 * OFOxmClassSerializerVer15.
+	 */
+	
+	/* String constants */
+	private static final String STR_MASKED = "_masked";
+	private static final String STR_NXM = "nxm_";
+	
+	/* Bit shifting constants */
+	private static final int SHIFT_FIELD = 9;
+	private static final int SHIFT_CLASS = 16;
+	private static final int SHIFT_HAS_MASK = 8;
+	
+	/* Mask present bit */
+	private static final int MASKED = (1 << SHIFT_HAS_MASK);
+	
+	/* Classes */
+	private static final int OF_BASIC = (OFOxmClassSerializerVer14.OPENFLOW_BASIC_VAL << SHIFT_CLASS) & 0xFFffFFff;
+	//TODO private static final int OF_PKT_REG = (0x8001 << SHIFT_CLASS) & 0xFFffFFff;
+	private static final int NXM_0 = (OFOxmClassSerializerVer14.NXM_0_VAL << SHIFT_CLASS) & 0xFFffFFff;
+	private static final int NXM_1 = (OFOxmClassSerializerVer14.NXM_1_VAL << SHIFT_CLASS) & 0xFFffFFff;
+	
+	/* The bi-directional map we'll use to make this process more efficient */
+	private static final BiMap<U32, String> map = HashBiMap.create();
+	static { 
+		/*
+		 * OXM header is 4 bytes as follows, where C=oxm_class,
+		 * F=oxm_field, M=has_mask, and L=data_length.
+		 * 
+		 * CCCC CCCC CCCC CCCC FFFF FFFM LLLL LLLL
+		 * 
+		 * The OXM class has already been shifted to the proper
+		 * position and is represented as an integer (4 bytes),
+		 * but we need to shift the OXM field over. The length
+		 * does not need to be shifted.
+		 * 
+		 */
+		
+		/*
+		 * OpenFlow Basic OXM Definitions
+		 */
+		map.put(U32.ofRaw(OF_BASIC | (0 << SHIFT_FIELD) | OFPort.ZERO.getLength()), MatchUtils.STR_IN_PORT);
+		map.put(U32.ofRaw(OF_BASIC | (0 << SHIFT_FIELD) | OFPort.ZERO.getLength() * 2 | MASKED), MatchUtils.STR_IN_PORT + STR_MASKED);
+		
+		map.put(U32.ofRaw(OF_BASIC | (1 << SHIFT_FIELD) | OFPort.ZERO.getLength()), MatchUtils.STR_IN_PHYS_PORT);
+		map.put(U32.ofRaw(OF_BASIC | (1 << SHIFT_FIELD) | OFPort.ZERO.getLength() * 2 | MASKED), MatchUtils.STR_IN_PHYS_PORT + STR_MASKED);
+		
+		map.put(U32.ofRaw(OF_BASIC | (2 << SHIFT_FIELD) | OFMetadata.NONE.getLength()), MatchUtils.STR_METADATA);
+		map.put(U32.ofRaw(OF_BASIC | (2 << SHIFT_FIELD) | OFMetadata.NONE.getLength() * 2 | MASKED), MatchUtils.STR_METADATA + STR_MASKED);
+		
+		map.put(U32.ofRaw(OF_BASIC | (3 << SHIFT_FIELD) | MacAddress.NONE.getLength()), MatchUtils.STR_DL_DST);
+		map.put(U32.ofRaw(OF_BASIC | (3 << SHIFT_FIELD) | MacAddress.NONE.getLength() * 2 | MASKED), MatchUtils.STR_DL_DST + STR_MASKED);
+		
+		map.put(U32.ofRaw(OF_BASIC | (4 << SHIFT_FIELD) | MacAddress.NONE.getLength()), MatchUtils.STR_DL_SRC);
+		map.put(U32.ofRaw(OF_BASIC | (4 << SHIFT_FIELD) | MacAddress.NONE.getLength() * 2 | MASKED), MatchUtils.STR_DL_SRC + STR_MASKED);
+		
+		map.put(U32.ofRaw(OF_BASIC | (5 << SHIFT_FIELD) | EthType.NONE.getLength()), MatchUtils.STR_DL_TYPE);
+		map.put(U32.ofRaw(OF_BASIC | (5 << SHIFT_FIELD) | EthType.NONE.getLength() * 2 | MASKED), MatchUtils.STR_DL_TYPE + STR_MASKED);
+		
+		map.put(U32.ofRaw(OF_BASIC | (6 << SHIFT_FIELD) | VlanVid.ZERO.getLength()), MatchUtils.STR_DL_VLAN);
+		map.put(U32.ofRaw(OF_BASIC | (6 << SHIFT_FIELD) | VlanVid.ZERO.getLength() * 2 | MASKED), MatchUtils.STR_DL_VLAN + STR_MASKED);
+		
+		map.put(U32.ofRaw(OF_BASIC | (7 << SHIFT_FIELD) | VlanPcp.NONE.getLength()), MatchUtils.STR_DL_VLAN_PCP);
+		map.put(U32.ofRaw(OF_BASIC | (7 << SHIFT_FIELD) | VlanPcp.NONE.getLength() * 2 | MASKED), MatchUtils.STR_DL_VLAN_PCP + STR_MASKED);
+		
+		map.put(U32.ofRaw(OF_BASIC | (8 << SHIFT_FIELD) | IpDscp.NONE.getLength()), MatchUtils.STR_NW_DSCP);
+		map.put(U32.ofRaw(OF_BASIC | (8 << SHIFT_FIELD) | IpDscp.NONE.getLength() * 2 | MASKED), MatchUtils.STR_NW_DSCP + STR_MASKED);
+		
+		map.put(U32.ofRaw(OF_BASIC | (9 << SHIFT_FIELD) | IpEcn.NONE.getLength()), MatchUtils.STR_NW_ECN);
+		map.put(U32.ofRaw(OF_BASIC | (9 << SHIFT_FIELD) | IpEcn.NONE.getLength() * 2 | MASKED), MatchUtils.STR_NW_ECN + STR_MASKED);
+		
+		map.put(U32.ofRaw(OF_BASIC | (10 << SHIFT_FIELD) | IpProtocol.NONE.getLength()), MatchUtils.STR_NW_PROTO);
+		map.put(U32.ofRaw(OF_BASIC | (10 << SHIFT_FIELD) | IpProtocol.NONE.getLength() * 2 | MASKED), MatchUtils.STR_NW_PROTO + STR_MASKED);
+		
+		map.put(U32.ofRaw(OF_BASIC | (11 << SHIFT_FIELD) | IPv4Address.NONE.getLength()), MatchUtils.STR_NW_SRC);
+		map.put(U32.ofRaw(OF_BASIC | (11 << SHIFT_FIELD) | IPv4Address.NONE.getLength() * 2 | MASKED), MatchUtils.STR_NW_SRC + STR_MASKED);
+		
+		map.put(U32.ofRaw(OF_BASIC | (12 << SHIFT_FIELD) | IPv4Address.NONE.getLength()), MatchUtils.STR_NW_DST);
+		map.put(U32.ofRaw(OF_BASIC | (12 << SHIFT_FIELD) | IPv4Address.NONE.getLength() * 2 | MASKED), MatchUtils.STR_NW_DST + STR_MASKED);
+		
+		map.put(U32.ofRaw(OF_BASIC | (13 << SHIFT_FIELD) | TransportPort.NONE.getLength()), MatchUtils.STR_TCP_SRC);
+		map.put(U32.ofRaw(OF_BASIC | (13 << SHIFT_FIELD) | TransportPort.NONE.getLength() * 2 | MASKED), MatchUtils.STR_TCP_SRC + STR_MASKED);
+		
+		map.put(U32.ofRaw(OF_BASIC | (14 << SHIFT_FIELD) | TransportPort.NONE.getLength()), MatchUtils.STR_TCP_DST);
+		map.put(U32.ofRaw(OF_BASIC | (14 << SHIFT_FIELD) | TransportPort.NONE.getLength() * 2 | MASKED), MatchUtils.STR_TCP_DST + STR_MASKED);
+		
+		map.put(U32.ofRaw(OF_BASIC | (15 << SHIFT_FIELD) | TransportPort.NONE.getLength()), MatchUtils.STR_UDP_SRC);
+		map.put(U32.ofRaw(OF_BASIC | (15 << SHIFT_FIELD) | TransportPort.NONE.getLength() * 2 | MASKED), MatchUtils.STR_UDP_SRC + STR_MASKED);
+		
+		map.put(U32.ofRaw(OF_BASIC | (16 << SHIFT_FIELD) | TransportPort.NONE.getLength()), MatchUtils.STR_UDP_DST);
+		map.put(U32.ofRaw(OF_BASIC | (16 << SHIFT_FIELD) | TransportPort.NONE.getLength() * 2 | MASKED), MatchUtils.STR_UDP_DST + STR_MASKED);
+		
+		map.put(U32.ofRaw(OF_BASIC | (17 << SHIFT_FIELD) | TransportPort.NONE.getLength()), MatchUtils.STR_SCTP_SRC);
+		map.put(U32.ofRaw(OF_BASIC | (17 << SHIFT_FIELD) | TransportPort.NONE.getLength() * 2 | MASKED), MatchUtils.STR_SCTP_SRC + STR_MASKED);
+		
+		map.put(U32.ofRaw(OF_BASIC | (18 << SHIFT_FIELD) | TransportPort.NONE.getLength()), MatchUtils.STR_SCTP_DST);
+		map.put(U32.ofRaw(OF_BASIC | (18 << SHIFT_FIELD) | TransportPort.NONE.getLength() * 2 | MASKED), MatchUtils.STR_SCTP_DST + STR_MASKED);
+		
+		map.put(U32.ofRaw(OF_BASIC | (19 << SHIFT_FIELD) | ICMPv4Type.NONE.getLength()), MatchUtils.STR_ICMP_TYPE);
+		map.put(U32.ofRaw(OF_BASIC | (19 << SHIFT_FIELD) | ICMPv4Type.NONE.getLength() * 2 | MASKED), MatchUtils.STR_ICMP_TYPE + STR_MASKED);
+		
+		map.put(U32.ofRaw(OF_BASIC | (20 << SHIFT_FIELD) | ICMPv4Code.NONE.getLength()), MatchUtils.STR_ICMP_CODE);
+		map.put(U32.ofRaw(OF_BASIC | (20 << SHIFT_FIELD) | ICMPv4Code.NONE.getLength() * 2 | MASKED), MatchUtils.STR_ICMP_CODE + STR_MASKED);
+		
+		map.put(U32.ofRaw(OF_BASIC | (21 << SHIFT_FIELD) | ArpOpcode.NONE.getLength()), MatchUtils.STR_ARP_OPCODE);
+		map.put(U32.ofRaw(OF_BASIC | (21 << SHIFT_FIELD) | ArpOpcode.NONE.getLength() * 2 | MASKED), MatchUtils.STR_ARP_OPCODE + STR_MASKED);
+		
+		map.put(U32.ofRaw(OF_BASIC | (22 << SHIFT_FIELD) | IPv4Address.NONE.getLength()), MatchUtils.STR_ARP_SPA);
+		map.put(U32.ofRaw(OF_BASIC | (22 << SHIFT_FIELD) | IPv4Address.NONE.getLength() * 2 | MASKED), MatchUtils.STR_ARP_SPA + STR_MASKED);
+		
+		map.put(U32.ofRaw(OF_BASIC | (23 << SHIFT_FIELD) | IPv4Address.NONE.getLength()), MatchUtils.STR_ARP_DPA);
+		map.put(U32.ofRaw(OF_BASIC | (23 << SHIFT_FIELD) | IPv4Address.NONE.getLength() * 2 | MASKED), MatchUtils.STR_ARP_DPA + STR_MASKED);
+		
+		map.put(U32.ofRaw(OF_BASIC | (24 << SHIFT_FIELD) | MacAddress.NONE.getLength()), MatchUtils.STR_ARP_SHA);
+		map.put(U32.ofRaw(OF_BASIC | (24 << SHIFT_FIELD) | MacAddress.NONE.getLength() * 2 | MASKED), MatchUtils.STR_ARP_SHA + STR_MASKED);
+		
+		map.put(U32.ofRaw(OF_BASIC | (25 << SHIFT_FIELD) | MacAddress.NONE.getLength()), MatchUtils.STR_ARP_DHA);
+		map.put(U32.ofRaw(OF_BASIC | (25 << SHIFT_FIELD) | MacAddress.NONE.getLength() * 2 | MASKED), MatchUtils.STR_ARP_DHA + STR_MASKED);
+		
+		map.put(U32.ofRaw(OF_BASIC | (26 << SHIFT_FIELD) | IPv6Address.NONE.getLength()), MatchUtils.STR_IPV6_SRC);
+		map.put(U32.ofRaw(OF_BASIC | (26 << SHIFT_FIELD) | IPv6Address.NONE.getLength() * 2 | MASKED), MatchUtils.STR_IPV6_SRC + STR_MASKED);
+		
+		
+		map.put(U32.ofRaw(OF_BASIC | (27 << SHIFT_FIELD) | IPv6Address.NONE.getLength()), MatchUtils.STR_IPV6_DST);
+		map.put(U32.ofRaw(OF_BASIC | (27 << SHIFT_FIELD) | IPv6Address.NONE.getLength() * 2 | MASKED), MatchUtils.STR_IPV6_DST + STR_MASKED);
+		
+		map.put(U32.ofRaw(OF_BASIC | (28 << SHIFT_FIELD) | IPv6FlowLabel.NONE.getLength()), MatchUtils.STR_IPV6_FLOW_LABEL);
+		map.put(U32.ofRaw(OF_BASIC | (28 << SHIFT_FIELD) | IPv6FlowLabel.NONE.getLength() * 2 | MASKED), MatchUtils.STR_IPV6_FLOW_LABEL + STR_MASKED);
+		
+		map.put(U32.ofRaw(OF_BASIC | (29 << SHIFT_FIELD) | U8.ZERO.getLength()), MatchUtils.STR_ICMPV6_TYPE);
+		map.put(U32.ofRaw(OF_BASIC | (29 << SHIFT_FIELD) | U8.ZERO.getLength() * 2 | MASKED), MatchUtils.STR_ICMPV6_TYPE + STR_MASKED);
+		
+		map.put(U32.ofRaw(OF_BASIC | (30 << SHIFT_FIELD) | U8.ZERO.getLength()), MatchUtils.STR_ICMPV6_CODE);
+		map.put(U32.ofRaw(OF_BASIC | (30 << SHIFT_FIELD) | U8.ZERO.getLength() * 2 | MASKED), MatchUtils.STR_ICMPV6_CODE + STR_MASKED);
+		
+		map.put(U32.ofRaw(OF_BASIC | (31 << SHIFT_FIELD) | IPv6Address.NONE.getLength()), MatchUtils.STR_IPV6_ND_TARGET);
+		map.put(U32.ofRaw(OF_BASIC | (31 << SHIFT_FIELD) | IPv6Address.NONE.getLength() * 2 | MASKED), MatchUtils.STR_IPV6_ND_TARGET + STR_MASKED);
+		
+		map.put(U32.ofRaw(OF_BASIC | (32 << SHIFT_FIELD) | MacAddress.NONE.getLength()), MatchUtils.STR_IPV6_ND_SSL);
+		map.put(U32.ofRaw(OF_BASIC | (32 << SHIFT_FIELD) | MacAddress.NONE.getLength() * 2 | MASKED), MatchUtils.STR_IPV6_ND_SSL + STR_MASKED);
+		
+		map.put(U32.ofRaw(OF_BASIC | (33 << SHIFT_FIELD) | MacAddress.NONE.getLength()), MatchUtils.STR_IPV6_ND_TTL);
+		map.put(U32.ofRaw(OF_BASIC | (33 << SHIFT_FIELD) | MacAddress.NONE.getLength() * 2 | MASKED), MatchUtils.STR_IPV6_ND_TTL + STR_MASKED);
+		
+		map.put(U32.ofRaw(OF_BASIC | (34 << SHIFT_FIELD) | U32.ZERO.getLength()), MatchUtils.STR_MPLS_LABEL);
+		map.put(U32.ofRaw(OF_BASIC | (34 << SHIFT_FIELD) | U32.ZERO.getLength() * 2 | MASKED), MatchUtils.STR_MPLS_LABEL + STR_MASKED);
+		
+		map.put(U32.ofRaw(OF_BASIC | (35 << SHIFT_FIELD) | U8.ZERO.getLength()), MatchUtils.STR_MPLS_TC);
+		map.put(U32.ofRaw(OF_BASIC | (35 << SHIFT_FIELD) | U8.ZERO.getLength() * 2 | MASKED), MatchUtils.STR_MPLS_TC + STR_MASKED);
+		
+		map.put(U32.ofRaw(OF_BASIC | (36 << SHIFT_FIELD) | OFBooleanValue.TRUE.getLength()), MatchUtils.STR_MPLS_BOS);
+		map.put(U32.ofRaw(OF_BASIC | (36 << SHIFT_FIELD) | OFBooleanValue.TRUE.getLength() * 2 | MASKED), MatchUtils.STR_MPLS_BOS + STR_MASKED);
+		
+		/*
+		 * TODO in Loxi and MatchUtils
+		 * map.put(U32.ofRaw(OF_BASIC | (37 << SHIFT_FIELD) | ), MatchUtils.STR_PBB_ISID);
+		 */
+		map.put(U32.ofRaw(OF_BASIC | (37 << SHIFT_FIELD) | 3 ), MatchUtils.STR_PBB_ISID); /* need to update after Loxi support */
+		map.put(U32.ofRaw(OF_BASIC | (37 << SHIFT_FIELD) | 3 * 2 | MASKED ), MatchUtils.STR_PBB_ISID + STR_MASKED);
+		
+		map.put(U32.ofRaw(OF_BASIC | (38 << SHIFT_FIELD) | U64.ZERO.getLength()), MatchUtils.STR_TUNNEL_ID);
+		map.put(U32.ofRaw(OF_BASIC | (38 << SHIFT_FIELD) | U64.ZERO.getLength() * 2 | MASKED), MatchUtils.STR_TUNNEL_ID + STR_MASKED);
+		
+		/* 
+		 * TODO in MatchUtils
+		 * map.put(U32.ofRaw(OF_BASIC | 39), MatchUtils.STR_IPV6_EXTHDR);
+		 */
+		map.put(U32.ofRaw(OF_BASIC | (39 << SHIFT_FIELD) | 2), "ipv6_exthdr"); /* need to update after Loxi support */
+		map.put(U32.ofRaw(OF_BASIC | (39 << SHIFT_FIELD) | 2 * 2 | MASKED), "ipv6_exthdr" + STR_MASKED);
+		
+		/* note skip of ID 40 here according to spec */
+		
+		/* 
+		 * TODO in MatchUtils
+		 * map.put(U32.ofRaw(OF_BASIC | 41), MatchUtils.STR_PBB_UCA);
+		 */
+		map.put(U32.ofRaw(OF_BASIC | (41 << SHIFT_FIELD) | 1), "pbb_uca");
+		map.put(U32.ofRaw(OF_BASIC | (41 << SHIFT_FIELD) | 1 * 2 | MASKED), "pbb_uca" + STR_MASKED);
+		
+		/*
+		 * NXM_0 Definitions
+		 */
+		map.put(U32.ofRaw(NXM_0 | (0 << SHIFT_FIELD) | 2 /* old ports are len=2 */), STR_NXM + MatchUtils.STR_IN_PORT);
+		map.put(U32.ofRaw(NXM_0 | (0 << SHIFT_FIELD) | 2 * 2 /* old ports are len=2 */ | MASKED), STR_NXM + MatchUtils.STR_IN_PORT + STR_MASKED);
+		
+		map.put(U32.ofRaw(NXM_0 | (1 << SHIFT_FIELD) | MacAddress.NONE.getLength()), STR_NXM + MatchUtils.STR_DL_DST);
+		map.put(U32.ofRaw(NXM_0 | (1 << SHIFT_FIELD) | MacAddress.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_DL_DST + STR_MASKED);
+		
+		map.put(U32.ofRaw(NXM_0 | (2 << SHIFT_FIELD) | MacAddress.NONE.getLength()), STR_NXM + MatchUtils.STR_DL_SRC);
+		map.put(U32.ofRaw(NXM_0 | (2 << SHIFT_FIELD) | MacAddress.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_DL_SRC + STR_MASKED);
+		
+		map.put(U32.ofRaw(NXM_0 | (3 << SHIFT_FIELD) | EthType.NONE.getLength()), STR_NXM + MatchUtils.STR_DL_TYPE);
+		map.put(U32.ofRaw(NXM_0 | (3 << SHIFT_FIELD) | EthType.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_DL_TYPE + STR_MASKED);
+		
+		map.put(U32.ofRaw(NXM_0 | (4 << SHIFT_FIELD) | VlanVid.ZERO.getLength()), STR_NXM + MatchUtils.STR_DL_VLAN);
+		map.put(U32.ofRaw(NXM_0 | (4 << SHIFT_FIELD) | VlanVid.ZERO.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_DL_VLAN + STR_MASKED);
+		
+		map.put(U32.ofRaw(NXM_0 | (5 << SHIFT_FIELD) | 1), STR_NXM + MatchUtils.STR_NW_TOS);
+		map.put(U32.ofRaw(NXM_0 | (5 << SHIFT_FIELD) | 1 * 2 | MASKED), STR_NXM + MatchUtils.STR_NW_TOS + STR_MASKED);
+		
+		map.put(U32.ofRaw(NXM_0 | (6 << SHIFT_FIELD) | IpProtocol.NONE.getLength()), STR_NXM + MatchUtils.STR_NW_PROTO);
+		map.put(U32.ofRaw(NXM_0 | (6 << SHIFT_FIELD) | IpProtocol.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_NW_PROTO + STR_MASKED);
+		
+		map.put(U32.ofRaw(NXM_0 | (7 << SHIFT_FIELD) | IPv4Address.NONE.getLength()), STR_NXM + MatchUtils.STR_NW_SRC);
+		map.put(U32.ofRaw(NXM_0 | (7 << SHIFT_FIELD) | IPv4Address.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_NW_SRC + STR_MASKED);
+		
+		map.put(U32.ofRaw(NXM_0 | (8 << SHIFT_FIELD) | IPv4Address.NONE.getLength()), STR_NXM + MatchUtils.STR_NW_DST);
+		map.put(U32.ofRaw(NXM_0 | (8 << SHIFT_FIELD) | IPv4Address.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_NW_DST + STR_MASKED);
+		
+		map.put(U32.ofRaw(NXM_0 | (9 << SHIFT_FIELD) | TransportPort.NONE.getLength()), STR_NXM + MatchUtils.STR_TCP_SRC);
+		map.put(U32.ofRaw(NXM_0 | (9 << SHIFT_FIELD) | TransportPort.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_TCP_SRC + STR_MASKED);
+		
+		map.put(U32.ofRaw(NXM_0 | (10 << SHIFT_FIELD) | TransportPort.NONE.getLength()), STR_NXM + MatchUtils.STR_TCP_DST);
+		map.put(U32.ofRaw(NXM_0 | (10 << SHIFT_FIELD) | TransportPort.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_TCP_DST + STR_MASKED);
+		
+		map.put(U32.ofRaw(NXM_0 | (11 << SHIFT_FIELD) | TransportPort.NONE.getLength()), STR_NXM + MatchUtils.STR_UDP_SRC);
+		map.put(U32.ofRaw(NXM_0 | (11 << SHIFT_FIELD) | TransportPort.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_UDP_SRC + STR_MASKED);
+		
+		map.put(U32.ofRaw(NXM_0 | (12 << SHIFT_FIELD) | TransportPort.NONE.getLength()), STR_NXM + MatchUtils.STR_UDP_DST);
+		map.put(U32.ofRaw(NXM_0 | (12 << SHIFT_FIELD) | TransportPort.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_UDP_DST + STR_MASKED);
+		
+		map.put(U32.ofRaw(NXM_0 | (13 << SHIFT_FIELD) | U8.ZERO.getLength()), STR_NXM + MatchUtils.STR_ICMP_TYPE);
+		map.put(U32.ofRaw(NXM_0 | (13 << SHIFT_FIELD) | U8.ZERO.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_ICMP_TYPE + STR_MASKED);
+		
+		map.put(U32.ofRaw(NXM_0 | (14 << SHIFT_FIELD) | U8.ZERO.getLength()), STR_NXM + MatchUtils.STR_ICMP_CODE);
+		map.put(U32.ofRaw(NXM_0 | (14 << SHIFT_FIELD) | U8.ZERO.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_ICMP_CODE + STR_MASKED);
+		
+		map.put(U32.ofRaw(NXM_0 | (15 << SHIFT_FIELD) | ArpOpcode.NONE.getLength()), STR_NXM + MatchUtils.STR_ARP_OPCODE);
+		map.put(U32.ofRaw(NXM_0 | (15 << SHIFT_FIELD) | ArpOpcode.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_ARP_OPCODE + STR_MASKED);
+		
+		map.put(U32.ofRaw(NXM_0 | (16 << SHIFT_FIELD) | IPv4Address.NONE.getLength()), STR_NXM + MatchUtils.STR_ARP_SPA);
+		map.put(U32.ofRaw(NXM_0 | (16 << SHIFT_FIELD) | IPv4Address.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_ARP_SPA + STR_MASKED);
+		
+		map.put(U32.ofRaw(NXM_0 | (17 << SHIFT_FIELD) | IPv4Address.NONE.getLength()), STR_NXM + MatchUtils.STR_ARP_DPA);
+		map.put(U32.ofRaw(NXM_0 | (17 << SHIFT_FIELD) | IPv4Address.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_ARP_DPA + STR_MASKED);
+
+		/*
+		 * NXM_1 Definitions
+		 */
+		map.put(U32.ofRaw(NXM_1 | (0 << SHIFT_FIELD) | 4), STR_NXM + "reg_0");
+		map.put(U32.ofRaw(NXM_1 | (0 << SHIFT_FIELD) | 4 * 2 | MASKED), STR_NXM + "reg_0" + STR_MASKED);
+		
+		map.put(U32.ofRaw(NXM_1 | (1 << SHIFT_FIELD) | 4), STR_NXM + "reg_1");
+		map.put(U32.ofRaw(NXM_1 | (1 << SHIFT_FIELD) | 4 * 2 | MASKED), STR_NXM + "reg_1" + STR_MASKED);
+		
+		map.put(U32.ofRaw(NXM_1 | (2 << SHIFT_FIELD) | 4), STR_NXM + "reg_2");
+		map.put(U32.ofRaw(NXM_1 | (2 << SHIFT_FIELD) | 4 * 2 | MASKED), STR_NXM + "reg_2" + STR_MASKED);
+		
+		map.put(U32.ofRaw(NXM_1 | (3 << SHIFT_FIELD) | 4), STR_NXM + "reg_3");
+		map.put(U32.ofRaw(NXM_1 | (3 << SHIFT_FIELD) | 4 * 2 | MASKED), STR_NXM + "reg_3" + STR_MASKED);
+		
+		map.put(U32.ofRaw(NXM_1 | (4 << SHIFT_FIELD) | 4), STR_NXM + "reg_4");
+		map.put(U32.ofRaw(NXM_1 | (4 << SHIFT_FIELD) | 4 * 2 | MASKED), STR_NXM + "reg_4" + STR_MASKED);
+		
+		map.put(U32.ofRaw(NXM_1 | (5 << SHIFT_FIELD) | 4), STR_NXM + "reg_5");
+		map.put(U32.ofRaw(NXM_1 | (5 << SHIFT_FIELD) | 4 * 2 | MASKED), STR_NXM + "reg_5" + STR_MASKED);
+		
+		map.put(U32.ofRaw(NXM_1 | (6 << SHIFT_FIELD) | 4), STR_NXM + "reg_6");
+		map.put(U32.ofRaw(NXM_1 | (6 << SHIFT_FIELD) | 4 * 2 | MASKED), STR_NXM + "reg_6" + STR_MASKED);
+		
+		map.put(U32.ofRaw(NXM_1 | (7 << SHIFT_FIELD) | 4), STR_NXM + "reg_7");
+		map.put(U32.ofRaw(NXM_1 | (7 << SHIFT_FIELD) | 4 * 2 | MASKED), STR_NXM + "reg_7" + STR_MASKED);
+
+		map.put(U32.ofRaw(NXM_1 | (16 << SHIFT_FIELD) | U64.ZERO.getLength()), STR_NXM + MatchUtils.STR_TUNNEL_ID);
+		map.put(U32.ofRaw(NXM_1 | (16 << SHIFT_FIELD) | U64.ZERO.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_TUNNEL_ID + STR_MASKED);
+		
+		map.put(U32.ofRaw(NXM_1 | (17 << SHIFT_FIELD) | MacAddress.NONE.getLength()), STR_NXM + MatchUtils.STR_ARP_SHA);
+		map.put(U32.ofRaw(NXM_1 | (17 << SHIFT_FIELD) | MacAddress.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_ARP_SHA + STR_MASKED);
+		
+		map.put(U32.ofRaw(NXM_1 | (18 << SHIFT_FIELD) | MacAddress.NONE.getLength()), STR_NXM + MatchUtils.STR_ARP_DHA);
+		map.put(U32.ofRaw(NXM_1 | (18 << SHIFT_FIELD) | MacAddress.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_ARP_DHA + STR_MASKED);
+		
+		map.put(U32.ofRaw(NXM_1 | (19 << SHIFT_FIELD) | IPv6Address.NONE.getLength()), STR_NXM + MatchUtils.STR_IPV6_SRC);
+		map.put(U32.ofRaw(NXM_1 | (19 << SHIFT_FIELD) | IPv6Address.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_IPV6_SRC + STR_MASKED);
+		
+		map.put(U32.ofRaw(NXM_1 | (20 << SHIFT_FIELD) | IPv6Address.NONE.getLength()), STR_NXM + MatchUtils.STR_IPV6_DST);
+		map.put(U32.ofRaw(NXM_1 | (20 << SHIFT_FIELD) | IPv6Address.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_IPV6_DST + STR_MASKED);
+		
+		map.put(U32.ofRaw(NXM_1 | (21 << SHIFT_FIELD) | U8.ZERO.getLength()), STR_NXM + MatchUtils.STR_ICMPV6_TYPE);
+		map.put(U32.ofRaw(NXM_1 | (21 << SHIFT_FIELD) | U8.ZERO.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_ICMPV6_TYPE + STR_MASKED);
+		
+		map.put(U32.ofRaw(NXM_1 | (22 << SHIFT_FIELD) | U8.ZERO.getLength()), STR_NXM + MatchUtils.STR_ICMPV6_CODE);
+		map.put(U32.ofRaw(NXM_1 | (22 << SHIFT_FIELD) | U8.ZERO.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_ICMPV6_CODE + STR_MASKED);
+		
+		map.put(U32.ofRaw(NXM_1 | (23 << SHIFT_FIELD) | IPv6Address.NONE.getLength()), STR_NXM + MatchUtils.STR_IPV6_ND_TARGET);
+		map.put(U32.ofRaw(NXM_1 | (23 << SHIFT_FIELD) | IPv6Address.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_IPV6_ND_TARGET + STR_MASKED);
+		
+		map.put(U32.ofRaw(NXM_1 | (24 << SHIFT_FIELD) | MacAddress.NONE.getLength()), STR_NXM + MatchUtils.STR_IPV6_ND_SSL);
+		map.put(U32.ofRaw(NXM_1 | (24 << SHIFT_FIELD) | MacAddress.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_IPV6_ND_SSL + STR_MASKED);
+		
+		map.put(U32.ofRaw(NXM_1 | (25 << SHIFT_FIELD) | MacAddress.NONE.getLength()), STR_NXM + MatchUtils.STR_IPV6_ND_TTL);
+		map.put(U32.ofRaw(NXM_1 | (25 << SHIFT_FIELD) | MacAddress.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_IPV6_ND_TTL + STR_MASKED);
+		
+		map.put(U32.ofRaw(NXM_1 | (26 << SHIFT_FIELD) | 1), STR_NXM + "ip_frag");
+		map.put(U32.ofRaw(NXM_1 | (26 << SHIFT_FIELD) | 1 * 2 | MASKED), STR_NXM + "ip_frag" + STR_MASKED);
+		
+		map.put(U32.ofRaw(NXM_1 | (27 << SHIFT_FIELD) | IPv6FlowLabel.NONE.getLength()), STR_NXM + MatchUtils.STR_IPV6_FLOW_LABEL);	
+		map.put(U32.ofRaw(NXM_1 | (27 << SHIFT_FIELD) | IPv6FlowLabel.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_IPV6_FLOW_LABEL + STR_MASKED);
+		
+		map.put(U32.ofRaw(NXM_1 | (28 << SHIFT_FIELD) | IpEcn.NONE.getLength()), STR_NXM + MatchUtils.STR_NW_ECN);
+		map.put(U32.ofRaw(NXM_1 | (28 << SHIFT_FIELD) | IpEcn.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_NW_ECN + STR_MASKED);
+		
+		map.put(U32.ofRaw(NXM_1 | (29 << SHIFT_FIELD) | 1), STR_NXM + "ip_ttl");
+		map.put(U32.ofRaw(NXM_1 | (29 << SHIFT_FIELD) | 1 * 2 | MASKED), STR_NXM + "ip_ttl" + STR_MASKED);
+		
+		map.put(U32.ofRaw(NXM_1 | (30 << SHIFT_FIELD) | 8), STR_NXM + "flow_cookie");
+		map.put(U32.ofRaw(NXM_1 | (30 << SHIFT_FIELD) | 8 * 2 | MASKED), STR_NXM + "flow_cookie" + STR_MASKED);
+		
+		map.put(U32.ofRaw(NXM_1 | (31 << SHIFT_FIELD) | IPv4Address.NONE.getLength()), MatchUtils.STR_TUNNEL_IPV4_SRC);
+		map.put(U32.ofRaw(NXM_1 | (31 << SHIFT_FIELD) | IPv4Address.NONE.getLength() * 2 | MASKED), MatchUtils.STR_TUNNEL_IPV4_SRC + MASKED);
+		
+		map.put(U32.ofRaw(NXM_1 | (32 << SHIFT_FIELD) | IPv4Address.NONE.getLength()), MatchUtils.STR_TUNNEL_IPV4_DST);
+		map.put(U32.ofRaw(NXM_1 | (32 << SHIFT_FIELD) | IPv4Address.NONE.getLength() * 2 | MASKED), MatchUtils.STR_TUNNEL_IPV4_DST + MASKED);
+		
+		map.put(U32.ofRaw(NXM_1 | (33 << SHIFT_FIELD) | 4), STR_NXM + "pkt_mark");
+		map.put(U32.ofRaw(NXM_1 | (33 << SHIFT_FIELD) | 4 * 2 | MASKED), STR_NXM + "pkt_mark" + STR_MASKED);
+		
+		map.put(U32.ofRaw(NXM_1 | (34 << SHIFT_FIELD) | 2), STR_NXM + "tcp_flags");
+		map.put(U32.ofRaw(NXM_1 | (34 << SHIFT_FIELD) | 2 * 2 | MASKED), STR_NXM + "tcp_flags" + STR_MASKED);
+		
+		map.put(U32.ofRaw(NXM_1 | (35 << SHIFT_FIELD) | 4), STR_NXM + "internal_dp_hash");
+		map.put(U32.ofRaw(NXM_1 | (35 << SHIFT_FIELD) | 4 * 2 | MASKED), STR_NXM + "internal_dp_hash" + STR_MASKED);
+		
+		map.put(U32.ofRaw(NXM_1 | (36 << SHIFT_FIELD) | 4), STR_NXM + "internal_recirc_id");
+		map.put(U32.ofRaw(NXM_1 | (36 << SHIFT_FIELD) | 4 * 2 | MASKED), STR_NXM + "internal_recirc_id" + STR_MASKED);
+		
+		map.put(U32.ofRaw(NXM_1 | (37 << SHIFT_FIELD) | 4), STR_NXM + MatchUtils.STR_PBB_ISID);
+		map.put(U32.ofRaw(NXM_1 | (37 << SHIFT_FIELD) | 4 * 2 | MASKED), STR_NXM + MatchUtils.STR_PBB_ISID + STR_MASKED);
+		
+	}
+	
+	/**
+	 * Converts the string representation of an OXM ID as
+	 * defined in {@link net.floodlightcontroller.util.MatchUtils.java}
+	 * to its corresponding U32 OXM ID.
+	 * 
+	 * Any NXM (not defined in {@link net.floodlightcontroller.util.MatchUtils.java}
+	 * is the same as the OXM string with an "nxm_" prefix.
+	 * 
+	 * Any OXM or NXM that is masked include a "_masked" postfix.
+	 * 
+	 * @param oxmId, the U32 defining the OXM/NXM as [class]+[field]+[has_mask]+[length]
+	 * @return the String representing the OXM/NXM
+	 */
+	public static String oxmIdToString(U32 oxmId) {
+		if (map.containsKey(oxmId)) {
+			return map.get(oxmId);
+		} else {
+			return "Unknown OXM ID: " + oxmId.toString();
+		}
+	}
+	
+	/**
+	 * Converts the string representation of an OXM ID as
+	 * defined in {@link net.floodlightcontroller.util.MatchUtils.java}
+	 * to its corresponding U32 OXM ID.
+	 * 
+	 * Any NXM (not defined in {@link net.floodlightcontroller.util.MatchUtils.java}
+	 * is the same as the OXM string with an "nxm_" prefix.
+	 * 
+	 * Any OXM or NXM that is masked include a "_masked" postfix.
+	 * 
+	 * @param oxmString, the string definging the OXM/NXM
+	 * @return the U32 representing the OXM/NXM as [class]+[field]+[has_mask]+[length]
+	 */
+	public static U32 oxmStringToId(String oxmString) {
+		if (map.inverse().containsKey(oxmString)) {
+			return map.inverse().get(oxmString);
+		} else {
+			return null;
+		}
+	}
+}
diff --git a/src/main/java/net/floodlightcontroller/core/web/serializers/StatsReplySerializer.java b/src/main/java/net/floodlightcontroller/core/web/serializers/StatsReplySerializer.java
index bd728ff80be82b0993fd10385b8dca53c63a0fed..3481e974505c4bcdfe50801a60a40848796c6ecd 100644
--- a/src/main/java/net/floodlightcontroller/core/web/serializers/StatsReplySerializer.java
+++ b/src/main/java/net/floodlightcontroller/core/web/serializers/StatsReplySerializer.java
@@ -158,7 +158,7 @@ public class StatsReplySerializer extends JsonSerializer<StatsReply> {
 
 		jGen.configure(Feature.WRITE_NUMBERS_AS_STRINGS, true); // IMHO this just looks nicer and is easier to read if everything is quoted
 		jGen.writeStartObject();
-		
+
 		if (reply.getStatType() == null) { // must be an OFFeaturesReply. getValues() was already checked for null above.
 			serializeFeaturesReply((OFFeaturesReply) reply.getValues(), jGen);
 		} else {
@@ -213,7 +213,7 @@ public class StatsReplySerializer extends JsonSerializer<StatsReply> {
 		}
 		jGen.writeEndObject();
 	}
-	
+
 	public static void serializeFeaturesReply(OFFeaturesReply fr, JsonGenerator jGen) throws IOException, JsonProcessingException {
 		/* Common to All OF Versions */			
 		jGen.writeStringField("capabilities", fr.getCapabilities().toString());
@@ -221,7 +221,7 @@ public class StatsReplySerializer extends JsonSerializer<StatsReply> {
 		jGen.writeNumberField("buffers", fr.getNBuffers());
 		jGen.writeNumberField("tables", fr.getNTables());
 		jGen.writeStringField("version", fr.getVersion().toString());
-		
+
 		if (fr.getVersion().compareTo(OFVersion.OF_13) < 0) { // OF1.3+ break this out into port_config
 			serializePortDesc(fr.getPorts(), jGen);
 		}
@@ -509,236 +509,216 @@ public class StatsReplySerializer extends JsonSerializer<StatsReply> {
 	 */
 	public static void serializeTableFeaturesReply(List<OFTableFeaturesStatsReply> tableFeaturesReplies, JsonGenerator jGen) throws IOException, JsonProcessingException{
 
-		OFTableFeaturesStatsReply tableFeaturesReply = tableFeaturesReplies.get(0);
-		jGen.writeStringField("version", tableFeaturesReply.getVersion().toString()); //return the enum name
-
 		jGen.writeFieldName("tableFeatures");
 		jGen.writeStartArray();
-		for(OFTableFeatures tableFeature : tableFeaturesReply.getEntries()) {
-			jGen.writeStartObject();                        
-			jGen.writeNumberField("tableId", tableFeature.getTableId().getValue());
-			jGen.writeStringField("name", tableFeature.getName());
-			jGen.writeNumberField("metadataMatch", tableFeature.getMetadataMatch().getValue());
-			jGen.writeNumberField("metadataWrite", tableFeature.getMetadataWrite().getValue());
-			jGen.writeNumberField("config", tableFeature.getConfig());
-			jGen.writeNumberField("maxEntries", tableFeature.getMaxEntries());
-
-			jGen.writeFieldName("properties");
-			jGen.writeStartArray();
-			for (OFTableFeatureProp properties : tableFeature.getProperties()) {            	
-				jGen.writeStartObject();
-				short type = (short)properties.getType();
-				jGen.writeNumberField("tableFeaturePropType",type);
-
-				switch (type) {
-				case OFTableFeaturePropTypeSerializerVer13.INSTRUCTIONS_VAL:
-					OFTableFeaturePropInstructions propInstruct = (OFTableFeaturePropInstructions) properties;
-					jGen.writeFieldName("instructions");
-					jGen.writeStartArray();
-					for (OFInstructionId id : propInstruct.getInstructionIds()) {
-						jGen.writeStartObject();
-						jGen.writeString(id.getType().toString());              			
-						jGen.writeEndObject();
-					}
-					jGen.writeEndArray();
-					break;
-				case OFTableFeaturePropTypeSerializerVer13.INSTRUCTIONS_MISS_VAL:
-					OFTableFeaturePropInstructionsMiss propInstructMiss = (OFTableFeaturePropInstructionsMiss) properties;
-					jGen.writeFieldName("instructionsMiss");
-					jGen.writeStartArray();
-					for (OFInstructionId id : propInstructMiss.getInstructionIds()) {
-						jGen.writeStartObject();
-						jGen.writeString(id.getType().toString());              			
-						jGen.writeEndObject();
-					}
-					jGen.writeEndArray();
-					break;
-				case OFTableFeaturePropTypeSerializerVer13.NEXT_TABLES_VAL:
-					OFTableFeaturePropNextTables propNxtTables = (OFTableFeaturePropNextTables) properties;
-					jGen.writeFieldName("nextTables");
-					jGen.writeStartArray();
-					for (U8 id : propNxtTables.getNextTableIds()) {
-						jGen.writeStartObject();
-						jGen.writeNumber(id.getValue());
-						jGen.writeEndObject();
-					}
-					jGen.writeEndArray();
-					break;
-				case OFTableFeaturePropTypeSerializerVer13.NEXT_TABLES_MISS_VAL:
-					OFTableFeaturePropNextTablesMiss propNxtTablesMiss = (OFTableFeaturePropNextTablesMiss) properties;
-					jGen.writeFieldName("nextTablesMiss");
-					jGen.writeStartArray();
-					for (U8 id : propNxtTablesMiss.getNextTableIds()) {
-						jGen.writeStartObject();
-						jGen.writeNumber(id.getValue());
-						jGen.writeEndObject();
-					}
-					jGen.writeEndArray();
-					break;
-				case OFTableFeaturePropTypeSerializerVer13.WRITE_ACTIONS_VAL:
-					OFTableFeaturePropWriteActions propWrAct = (OFTableFeaturePropWriteActions) properties; 
-					jGen.writeFieldName("writeActions");
-					jGen.writeStartArray();
-					for (OFActionId id : propWrAct.getActionIds()) {
-						jGen.writeStartObject();
-						jGen.writeString(id.getType().toString());
-						jGen.writeEndObject();
-					}
-					jGen.writeEndArray();
-					break;
-				case OFTableFeaturePropTypeSerializerVer13.WRITE_ACTIONS_MISS_VAL:
-					OFTableFeaturePropWriteActionsMiss propWrActMiss = (OFTableFeaturePropWriteActionsMiss) properties;
-					jGen.writeFieldName("writeActionsMiss");
-					jGen.writeStartArray();
-					for (OFActionId id : propWrActMiss.getActionIds()) {
-						jGen.writeStartObject();
-						jGen.writeString(id.getType().toString());
-						jGen.writeEndObject();
-					}
-					jGen.writeEndArray();
-					break;
-				case OFTableFeaturePropTypeSerializerVer13.APPLY_ACTIONS_VAL:
-					OFTableFeaturePropApplyActions propAppAct = (OFTableFeaturePropApplyActions) properties;   
-					jGen.writeFieldName("applyActions");
-					jGen.writeStartArray();
-					for (OFActionId id : propAppAct.getActionIds()) {
-						jGen.writeStartObject();
-						jGen.writeString(id.getType().toString());
-						jGen.writeEndObject();
-					}
-					jGen.writeEndArray();
-					break;	
-				case OFTableFeaturePropTypeSerializerVer13.APPLY_ACTIONS_MISS_VAL:
-					OFTableFeaturePropApplyActionsMiss propAppActMiss = (OFTableFeaturePropApplyActionsMiss) properties;
-					jGen.writeFieldName("applyActionsMiss");
-					jGen.writeStartArray();
-					for (OFActionId id : propAppActMiss.getActionIds()) {
-						jGen.writeStartObject();
-						jGen.writeString(id.getType().toString());
-						jGen.writeEndObject();
-					}
-					jGen.writeEndArray();
-					break;
-				case OFTableFeaturePropTypeSerializerVer13.MATCH_VAL:                	
-					OFTableFeaturePropMatch propMatch = (OFTableFeaturePropMatch) properties;
-					jGen.writeFieldName("match");
-					jGen.writeStartArray();
-					for (U32 id : propMatch.getOxmIds()) {
-						jGen.writeStartObject();
-						jGen.writeNumber(id.getValue());
-						jGen.writeEndObject();
-					}
-					jGen.writeEndArray();
-					break;
-				case OFTableFeaturePropTypeSerializerVer13.WILDCARDS_VAL:
-					OFTableFeaturePropWildcards propWildcards = (OFTableFeaturePropWildcards) properties;
-					jGen.writeFieldName("wildcards");
-					jGen.writeStartArray();
-					for (U32 id : propWildcards.getOxmIds()) {
-						jGen.writeStartObject();
-						jGen.writeNumber(id.getValue());
-						jGen.writeEndObject();
-					}
-					jGen.writeEndArray();
-					break;
-				case OFTableFeaturePropTypeSerializerVer13.WRITE_SETFIELD_VAL:
-					OFTableFeaturePropWriteSetfield propWrSetfield = (OFTableFeaturePropWriteSetfield) properties;           
-					jGen.writeFieldName("writeSetfield");
-					jGen.writeStartArray();
-					for (U32 id : propWrSetfield.getOxmIds()) {
-						jGen.writeStartObject();
-						jGen.writeNumber(id.getValue());
-						jGen.writeEndObject();
-					}
-					jGen.writeEndArray();
-					break;
-				case OFTableFeaturePropTypeSerializerVer13.WRITE_SETFIELD_MISS_VAL:
-					OFTableFeaturePropWriteSetfieldMiss propWrSetfieldMiss = (OFTableFeaturePropWriteSetfieldMiss) properties; 
-					jGen.writeFieldName("writeSetfieldMiss");
-					jGen.writeStartArray();
-					for (U32 id : propWrSetfieldMiss.getOxmIds()) {
+		for (OFTableFeaturesStatsReply tableFeaturesReply : tableFeaturesReplies) {
+
+			for(OFTableFeatures tableFeature : tableFeaturesReply.getEntries()) {
+				jGen.writeStartObject();    
+				jGen.writeStringField("version", tableFeature.getVersion().toString());
+				jGen.writeNumberField("tableId", tableFeature.getTableId().getValue());
+				jGen.writeStringField("name", tableFeature.getName());
+				jGen.writeNumberField("metadataMatch", tableFeature.getMetadataMatch().getValue());
+				jGen.writeNumberField("metadataWrite", tableFeature.getMetadataWrite().getValue());
+				jGen.writeNumberField("config", tableFeature.getConfig());
+				jGen.writeNumberField("maxEntries", tableFeature.getMaxEntries());
+
+				jGen.writeFieldName("properties");
+				jGen.writeStartArray();
+				for (OFTableFeatureProp properties : tableFeature.getProperties()) {            	
+					jGen.writeStartObject();
+
+					short type = (short)properties.getType();
+					switch (type) {
+					case OFTableFeaturePropTypeSerializerVer13.INSTRUCTIONS_VAL:
+						OFTableFeaturePropInstructions propInstruct = (OFTableFeaturePropInstructions) properties;
+						jGen.writeFieldName("instructions");
+						jGen.writeStartArray();
+						for (OFInstructionId id : propInstruct.getInstructionIds()) {
+							jGen.writeString(id.getType().toString());              			
+						}
+						jGen.writeEndArray();
+						break;
+					case OFTableFeaturePropTypeSerializerVer13.INSTRUCTIONS_MISS_VAL:
+						OFTableFeaturePropInstructionsMiss propInstructMiss = (OFTableFeaturePropInstructionsMiss) properties;
+						jGen.writeFieldName("instructionsMiss");
+						jGen.writeStartArray();
+						for (OFInstructionId id : propInstructMiss.getInstructionIds()) {
+							jGen.writeString(id.getType().toString());              			
+						}
+						jGen.writeEndArray();
+						break;
+					case OFTableFeaturePropTypeSerializerVer13.NEXT_TABLES_VAL:
+						OFTableFeaturePropNextTables propNxtTables = (OFTableFeaturePropNextTables) properties;
+						jGen.writeFieldName("nextTables");
+						jGen.writeStartArray();
+						for (U8 id : propNxtTables.getNextTableIds()) {
+							jGen.writeNumber(id.getValue());
+						}
+						jGen.writeEndArray();
+						break;
+					case OFTableFeaturePropTypeSerializerVer13.NEXT_TABLES_MISS_VAL:
+						OFTableFeaturePropNextTablesMiss propNxtTablesMiss = (OFTableFeaturePropNextTablesMiss) properties;
+						jGen.writeFieldName("nextTablesMiss");
+						jGen.writeStartArray();
+						for (U8 id : propNxtTablesMiss.getNextTableIds()) {
+							jGen.writeNumber(id.getValue());
+						}
+						jGen.writeEndArray();
+						break;
+					case OFTableFeaturePropTypeSerializerVer13.WRITE_ACTIONS_VAL:
+						OFTableFeaturePropWriteActions propWrAct = (OFTableFeaturePropWriteActions) properties; 
+						jGen.writeFieldName("writeActions");
+						jGen.writeStartArray();
+						for (OFActionId id : propWrAct.getActionIds()) {
+							jGen.writeString(id.getType().toString());
+						}
+						jGen.writeEndArray();
+						break;
+					case OFTableFeaturePropTypeSerializerVer13.WRITE_ACTIONS_MISS_VAL:
+						OFTableFeaturePropWriteActionsMiss propWrActMiss = (OFTableFeaturePropWriteActionsMiss) properties;
+						jGen.writeFieldName("writeActionsMiss");
+						jGen.writeStartArray();
+						for (OFActionId id : propWrActMiss.getActionIds()) {
+							jGen.writeString(id.getType().toString());
+						}
+						jGen.writeEndArray();
+						break;
+					case OFTableFeaturePropTypeSerializerVer13.APPLY_ACTIONS_VAL:
+						OFTableFeaturePropApplyActions propAppAct = (OFTableFeaturePropApplyActions) properties;   
+						jGen.writeFieldName("applyActions");
+						jGen.writeStartArray();
+						for (OFActionId id : propAppAct.getActionIds()) {
+							jGen.writeString(id.getType().toString());
+						}
+						jGen.writeEndArray();
+						break;	
+					case OFTableFeaturePropTypeSerializerVer13.APPLY_ACTIONS_MISS_VAL:
+						OFTableFeaturePropApplyActionsMiss propAppActMiss = (OFTableFeaturePropApplyActionsMiss) properties;
+						jGen.writeFieldName("applyActionsMiss");
+						jGen.writeStartArray();
+						for (OFActionId id : propAppActMiss.getActionIds()) {
+							jGen.writeString(id.getType().toString());
+						}
+						jGen.writeEndArray();
+						break;
+					case OFTableFeaturePropTypeSerializerVer13.MATCH_VAL:                	
+						OFTableFeaturePropMatch propMatch = (OFTableFeaturePropMatch) properties;
+						jGen.writeFieldName("match");
+						jGen.writeStartArray();
+						for (U32 id : propMatch.getOxmIds()) {
+							jGen.writeString(OXMSerializer.oxmIdToString(id));
+						}
+						jGen.writeEndArray();
+						break;
+					case OFTableFeaturePropTypeSerializerVer13.WILDCARDS_VAL:
+						OFTableFeaturePropWildcards propWildcards = (OFTableFeaturePropWildcards) properties;
+						jGen.writeFieldName("wildcards");
+						jGen.writeStartArray();
+						for (U32 id : propWildcards.getOxmIds()) {
+							jGen.writeString(OXMSerializer.oxmIdToString(id));
+						}
+						jGen.writeEndArray();
+						break;
+					case OFTableFeaturePropTypeSerializerVer13.WRITE_SETFIELD_VAL:
+						OFTableFeaturePropWriteSetfield propWrSetfield = (OFTableFeaturePropWriteSetfield) properties;           
+						jGen.writeFieldName("writeSetfield");
+						jGen.writeStartArray();
+						for (U32 id : propWrSetfield.getOxmIds()) {
+							jGen.writeString(OXMSerializer.oxmIdToString(id));
+						}
+						jGen.writeEndArray();
+						break;
+					case OFTableFeaturePropTypeSerializerVer13.WRITE_SETFIELD_MISS_VAL:
+						OFTableFeaturePropWriteSetfieldMiss propWrSetfieldMiss = (OFTableFeaturePropWriteSetfieldMiss) properties; 
+						jGen.writeFieldName("writeSetfieldMiss");
+						jGen.writeStartArray();
+						for (U32 id : propWrSetfieldMiss.getOxmIds()) {
+							jGen.writeString(OXMSerializer.oxmIdToString(id));
+						}
+						jGen.writeEndArray();
+						break;
+					case OFTableFeaturePropTypeSerializerVer13.APPLY_SETFIELD_VAL:
+						OFTableFeaturePropApplySetfield propAppSetfield = (OFTableFeaturePropApplySetfield) properties;
+						jGen.writeFieldName("applySetfield");
+						jGen.writeStartArray();
+						for (U32 id : propAppSetfield.getOxmIds()) {
+							jGen.writeString(OXMSerializer.oxmIdToString(id));
+						}
+						jGen.writeEndArray();
+						break;
+					case OFTableFeaturePropTypeSerializerVer13.APPLY_SETFIELD_MISS_VAL:
+						OFTableFeaturePropApplySetfieldMiss propAppSetfieldMiss = (OFTableFeaturePropApplySetfieldMiss) properties;                		
+						jGen.writeFieldName("applySetfieldMiss");
+						jGen.writeStartArray();
+						for (U32 id : propAppSetfieldMiss.getOxmIds()) {
+							jGen.writeString(OXMSerializer.oxmIdToString(id));
+						}
+						jGen.writeEndArray();
+						break;
+					case OFTableFeaturePropTypeSerializerVer13.EXPERIMENTER_VAL:
+						OFTableFeaturePropExperimenter propExp = (OFTableFeaturePropExperimenter) properties; 
+						jGen.writeFieldName("experimenter");
 						jGen.writeStartObject();
-						jGen.writeNumber(id.getValue());
+						jGen.writeNumberField("subType", propExp.getSubtype());
+						jGen.writeNumberField("experimenter", propExp.getExperimenter());
+						jGen.writeStringField("data", propExp.getExperimenterData().toString());
 						jGen.writeEndObject();
-					}
-					jGen.writeEndArray();
-					break;
-				case OFTableFeaturePropTypeSerializerVer13.APPLY_SETFIELD_VAL:
-					OFTableFeaturePropApplySetfield propAppSetfield = (OFTableFeaturePropApplySetfield) properties;
-					jGen.writeFieldName("applySetfield");
-					jGen.writeStartArray();
-					for (U32 id : propAppSetfield.getOxmIds()) {
+						break;	
+					case OFTableFeaturePropTypeSerializerVer13.EXPERIMENTER_MISS_VAL:
+						OFTableFeaturePropExperimenterMiss propExpMiss = (OFTableFeaturePropExperimenterMiss) properties;
+						jGen.writeFieldName("experimenterMiss");
 						jGen.writeStartObject();
-						jGen.writeNumber(id.getValue());
+						jGen.writeNumberField("subType", propExpMiss.getSubtype());
+						jGen.writeNumberField("experimenter", propExpMiss.getExperimenter());
+						jGen.writeStringField("data", propExpMiss.getExperimenterData().toString());
 						jGen.writeEndObject();
-					}
-					jGen.writeEndArray();
-					break;
-				case OFTableFeaturePropTypeSerializerVer13.APPLY_SETFIELD_MISS_VAL:
-					OFTableFeaturePropApplySetfieldMiss propAppSetfieldMiss = (OFTableFeaturePropApplySetfieldMiss) properties;                		
-					jGen.writeFieldName("applySetfieldMiss");
-					jGen.writeStartArray();
-					for (U32 id : propAppSetfieldMiss.getOxmIds()) {
+						break;	
+					default:
+						// shouldn't ever get here
 						jGen.writeStartObject();
-						jGen.writeNumber(id.getValue());
 						jGen.writeEndObject();
-					}
-					jGen.writeEndArray();
-					break;
-				case OFTableFeaturePropTypeSerializerVer13.EXPERIMENTER_VAL:
-					OFTableFeaturePropExperimenter propExp = (OFTableFeaturePropExperimenter) properties; 
-					jGen.writeFieldName("experimenter");
-					jGen.writeStartObject();
-					jGen.writeNumberField("subType", propExp.getSubtype());
-					jGen.writeNumberField("experimenter", propExp.getExperimenter());
-					jGen.writeStringField("subType", propExp.getExperimenterData().toString());
+						break;            		
+					}//end of Switch Case  
 					jGen.writeEndObject();
-					break;	
-				case OFTableFeaturePropTypeSerializerVer13.EXPERIMENTER_MISS_VAL:
-					OFTableFeaturePropExperimenterMiss propExpMiss = (OFTableFeaturePropExperimenterMiss) properties;
-					jGen.writeFieldName("experimenterMiss");
-					jGen.writeStartObject();
-					jGen.writeNumberField("subType", propExpMiss.getSubtype());
-					jGen.writeNumberField("experimenter", propExpMiss.getExperimenter());
-					jGen.writeStringField("subType", propExpMiss.getExperimenterData().toString());
-					jGen.writeEndObject();
-					break;	
-				default:
-					// shouldn't ever get here
-					break;            		
-				}//end of Switch Case  
+				}//end of for loop - properties                                              
+				jGen.writeEndArray();
 				jGen.writeEndObject();
-			}//end of for loop - properties                                              
-			jGen.writeEndObject();
-		}//end of for loop - features
+			}//end of for loop - features
+		} //end of looping through REQ_MORE flagged message loop
 		jGen.writeEndArray();
 	} 
 
-
-	public static void serializePortReply(List<OFPortStatsReply> portReplies, JsonGenerator jGen) throws IOException, JsonProcessingException{
-		OFPortStatsReply portReply = portReplies.get(0); // we will get only one PortReply and it will contains many OFPortStatsEntry ?
-		jGen.writeStringField("version", portReply.getVersion().toString()); //return the enum name
-		jGen.writeFieldName("port");
+	public static void serializePortReply(List<OFPortStatsReply> portReplies, JsonGenerator jGen) throws IOException, JsonProcessingException{		
+		jGen.writeFieldName("port_reply");
 		jGen.writeStartArray();
-		for(OFPortStatsEntry entry : portReply.getEntries()) {
+		for (OFPortStatsReply portReply : portReplies) {
 			jGen.writeStartObject();
-			jGen.writeStringField("portNumber",entry.getPortNo().toString());
-			jGen.writeNumberField("receivePackets", entry.getRxPackets().getValue());
-			jGen.writeNumberField("transmitPackets", entry.getTxPackets().getValue());
-			jGen.writeNumberField("receiveBytes", entry.getRxBytes().getValue());
-			jGen.writeNumberField("transmitBytes", entry.getTxBytes().getValue());
-			jGen.writeNumberField("receiveDropped", entry.getRxDropped().getValue());
-			jGen.writeNumberField("transmitDropped", entry.getTxDropped().getValue());
-			jGen.writeNumberField("receiveErrors", entry.getRxErrors().getValue());
-			jGen.writeNumberField("transmitErrors", entry.getTxErrors().getValue());
-			jGen.writeNumberField("receiveFrameErrors", entry.getRxFrameErr().getValue());
-			jGen.writeNumberField("receiveOverrunErrors", entry.getRxOverErr().getValue());
-			jGen.writeNumberField("receiveCRCErrors", entry.getRxCrcErr().getValue());
-			jGen.writeNumberField("collisions", entry.getCollisions().getValue());
-			if (OFVersion.OF_13 == entry.getVersion()) {
-				jGen.writeNumberField("durationSec", entry.getDurationSec());
-				jGen.writeNumberField("durationNsec", entry.getDurationNsec());
+			jGen.writeStringField("version", portReply.getVersion().toString()); //return the enum name
+			jGen.writeFieldName("port");
+			jGen.writeStartArray();
+			for(OFPortStatsEntry entry : portReply.getEntries()) {
+				jGen.writeStartObject();
+				jGen.writeStringField("portNumber",entry.getPortNo().toString());
+				jGen.writeNumberField("receivePackets", entry.getRxPackets().getValue());
+				jGen.writeNumberField("transmitPackets", entry.getTxPackets().getValue());
+				jGen.writeNumberField("receiveBytes", entry.getRxBytes().getValue());
+				jGen.writeNumberField("transmitBytes", entry.getTxBytes().getValue());
+				jGen.writeNumberField("receiveDropped", entry.getRxDropped().getValue());
+				jGen.writeNumberField("transmitDropped", entry.getTxDropped().getValue());
+				jGen.writeNumberField("receiveErrors", entry.getRxErrors().getValue());
+				jGen.writeNumberField("transmitErrors", entry.getTxErrors().getValue());
+				jGen.writeNumberField("receiveFrameErrors", entry.getRxFrameErr().getValue());
+				jGen.writeNumberField("receiveOverrunErrors", entry.getRxOverErr().getValue());
+				jGen.writeNumberField("receiveCRCErrors", entry.getRxCrcErr().getValue());
+				jGen.writeNumberField("collisions", entry.getCollisions().getValue());
+				if (OFVersion.OF_13 == entry.getVersion()) {
+					jGen.writeNumberField("durationSec", entry.getDurationSec());
+					jGen.writeNumberField("durationNsec", entry.getDurationNsec());
+				}
+				jGen.writeEndObject();
 			}
+			jGen.writeEndArray();
 			jGen.writeEndObject();
 		}
 		jGen.writeEndArray();
@@ -847,7 +827,7 @@ public class StatsReplySerializer extends JsonSerializer<StatsReply> {
 		jGen.writeStringField("version", portDescReply.getVersion().toString()); //return the enum name
 		serializePortDesc(portDescReply.getEntries(), jGen);
 	}
-	
+
 	public static void serializePortDesc(List<OFPortDesc> portDescList, JsonGenerator jGen) throws IOException, JsonProcessingException {
 		jGen.writeFieldName("portDesc");
 		jGen.writeStartArray();
diff --git a/src/main/java/net/floodlightcontroller/debugevent/IDebugEventService.java b/src/main/java/net/floodlightcontroller/debugevent/IDebugEventService.java
index c2a0fcdd95db1235614011b4d97713d31ba28162..9016edab905e493f4f5e64b0e041b3adcf56c53e 100644
--- a/src/main/java/net/floodlightcontroller/debugevent/IDebugEventService.java
+++ b/src/main/java/net/floodlightcontroller/debugevent/IDebugEventService.java
@@ -32,7 +32,7 @@ public interface IDebugEventService extends IFloodlightService {
      * Describes the type of field obtained from reflection
      */
     enum EventFieldType {
-        DPID, IPv4, MAC, STRING, OBJECT, PRIMITIVE, COLLECTION_IPV4,
+        DPID, IPv4, IPv6, MAC, STRING, OBJECT, PRIMITIVE, COLLECTION_IPV4,
         COLLECTION_ATTACHMENT_POINT, COLLECTION_OBJECT, SREF_COLLECTION_OBJECT,
         SREF_OBJECT
     }
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/IDevice.java b/src/main/java/net/floodlightcontroller/devicemanager/IDevice.java
index 4d5ba9be6d5d5ef7f31a4316cb92d2303a62502f..072808dfbf88cc4be941a883f02aec17e66611ab 100644
--- a/src/main/java/net/floodlightcontroller/devicemanager/IDevice.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/IDevice.java
@@ -20,6 +20,7 @@ package net.floodlightcontroller.devicemanager;
 import java.util.Date;
 
 import org.projectfloodlight.openflow.types.IPv4Address;
+import org.projectfloodlight.openflow.types.IPv6Address;
 import org.projectfloodlight.openflow.types.MacAddress;
 import org.projectfloodlight.openflow.types.VlanVid;
 
@@ -62,12 +63,19 @@ public interface IDevice {
      */
     public IPv4Address[] getIPv4Addresses();
     
+    /**
+     * Get all unique IPv6 addresses associated with the device.
+     * @return an array containing the unique IPv6 addresses for the device.
+     */
+    public IPv6Address[] getIPv6Addresses();
+    
     /**
      * Get all unique attachment points associated with the device.  This will
      * not include any blocked attachment points.
      * @return an array containing all unique attachment points for the device
      */
     public SwitchPort[] getAttachmentPoints();
+    
     /**
      * Get all old attachment points associated with the device.  this is used in host movement scenario.
      * @return an array containing all unique old attachment points for the device
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/IDeviceListener.java b/src/main/java/net/floodlightcontroller/devicemanager/IDeviceListener.java
index 1221e66887666765574bc543cd57a049e839f616..19b7c85bbdd1287074101b0cb83bceeff06287e5 100644
--- a/src/main/java/net/floodlightcontroller/devicemanager/IDeviceListener.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/IDeviceListener.java
@@ -55,6 +55,13 @@ public interface IDeviceListener extends IListener<String> {
      */
     public void deviceIPV4AddrChanged(IDevice device);
     
+    /**
+     * Called when a network address has been added or remove from a device
+     * 
+     * @param device the device that changed
+     */
+    public void deviceIPV6AddrChanged(IDevice device);
+    
     /**
      * Called when a VLAN tag for the device has been added or removed
      * @param device the device that changed
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/IDeviceService.java b/src/main/java/net/floodlightcontroller/devicemanager/IDeviceService.java
index 40d729f6894149d9a1e3142c618bc606c0753a4b..54d6963fd6440ef3a389ce746afe93acd1e4dc25 100755
--- a/src/main/java/net/floodlightcontroller/devicemanager/IDeviceService.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/IDeviceService.java
@@ -22,8 +22,11 @@ import java.util.EnumSet;
 import java.util.Iterator;
 import java.util.Set;
 
+import javax.annotation.Nonnull;
+
 import org.projectfloodlight.openflow.types.DatapathId;
 import org.projectfloodlight.openflow.types.IPv4Address;
+import org.projectfloodlight.openflow.types.IPv6Address;
 import org.projectfloodlight.openflow.types.MacAddress;
 import org.projectfloodlight.openflow.types.VlanVid;
 import org.projectfloodlight.openflow.types.OFPort;
@@ -43,7 +46,7 @@ public interface IDeviceService extends IFloodlightService {
      * @see IDeviceService#addIndex
      */
     enum DeviceField {
-        MAC, IPV4, VLAN, SWITCH, PORT
+        MAC, IPv4, IPv6, VLAN, SWITCH, PORT
     }
 
     /**
@@ -85,25 +88,26 @@ public interface IDeviceService extends IFloodlightService {
      * is the same lookup process that is used for packet_in processing and 
      * device learning. Thus, findDevice() can be used to match flow entries
      * from switches to devices. 
+     * 
      * Only the key fields as defined by the {@link IEntityClassifierService} will
-     * be important in this search. All key fields MUST be supplied. 
+     * be important in this search. All key fields MUST be supplied.
      * 
      *{@link queryDevices()} might be more appropriate!
      * 
-     * @param macAddress The MAC address
-     * @param vlan the VLAN. Null means no VLAN and is valid even if VLAN is a 
-     *        key field.
-     * @param ipv4Address the ipv4 address
-     * @param switchDPID the switch DPID
-     * @param switchPort the switch port
+     * @param macAddress the MAC address; use MacAddress.NONE for 'don't care'
+     * @param vlan the VLAN; use VlanVid.ZERO for 'untagged'
+     * @param ipv4Address the ipv4 address; use IPv4Address.NONE for 'don't care'
+     * @param ipv6Address the ipv6 address; use IPv6Address.NONE for 'don't care'
+     * @param switchDPID the switch DPID; use DatapathId.NONE for 'don't care'
+     * @param switchPort the switch port; use OFPort.ZERO for 'don't care'
      * @return an {@link IDevice} or null if no device is found.
      * @see IDeviceManager#setEntityClassifier(IEntityClassifierService)
      * @throws IllegalArgumentException if not all key fields of the
      * current {@link IEntityClassifierService} are specified.
      */
-    public IDevice findDevice(MacAddress macAddress, VlanVid vlan,
-                              IPv4Address ipv4Address, DatapathId switchDPID,
-                              OFPort switchPort)
+    public IDevice findDevice(@Nonnull MacAddress macAddress, VlanVid vlan,
+                              @Nonnull IPv4Address ipv4Address, @Nonnull IPv6Address ipv6Address,
+                              @Nonnull DatapathId switchDPID, @Nonnull OFPort switchPort)
                               throws IllegalArgumentException;
     
     /**
@@ -118,18 +122,18 @@ public interface IDeviceService extends IFloodlightService {
      * be important in this search. All key fields MUST be supplied.
      * 
      * @param entityClass The entity class in which to perform the lookup.
-     * @param macAddress The MAC address for the destination
-     * @param vlan the VLAN if available
-     * @param ipv4Address The IP address if available.
+     * @param macAddress the MAC address; use MacAddress.NONE for 'don't care'
+     * @param vlan the VLAN; use VlanVid.ZERO for 'untagged'
+     * @param ipv4Address the ipv4 address; use IPv4Address.NONE for 'don't care'
+     * @param ipv6Address the ipv6 address; use IPv6Address.NONE for 'don't care'
      * @return an {@link IDevice} or null if no device is found.
-     * @see IDeviceService#findDevice(long, Short, Integer, Long, 
-     * Integer)
+     * @see IDeviceService#findDevice(MacAddress, VlanVid, IPv4Address, IPv6Address, DatapathId, OFPort)
      * @throws IllegalArgumentException if not all key fields of the
      * source's {@link IEntityClass} are specified.
      */
-    public IDevice findClassDevice(IEntityClass entityClass,
-                                   MacAddress macAddress, VlanVid vlan,
-                                   IPv4Address ipv4Address)
+    public IDevice findClassDevice(@Nonnull IEntityClass entityClass,
+                                   @Nonnull MacAddress macAddress, VlanVid vlan,
+                                   @Nonnull IPv4Address ipv4Address, @Nonnull IPv6Address ipv6Address)
                                    throws IllegalArgumentException;
 
     /**
@@ -159,20 +163,22 @@ public interface IDeviceService extends IFloodlightService {
      * the query, then it will be performed efficiently using the index.
      * Otherwise, there will be a full scan of the device list.
      * 
-     * @param macAddress The MAC address
-     * @param vlan the VLAN
-     * @param ipv4Address the ipv4 address
-     * @param switchDPID the switch DPID
-     * @param switchPort the switch port
+     * @param macAddress the MAC address; use MacAddress.NONE for 'don't care'
+     * @param vlan the VLAN; use null for 'don't care'; use VlanVid.ZERO for 'untagged'
+     * @param ipv4Address the ipv4 address; use IPv4Address.NONE for 'don't care'
+     * @param ipv6Address the ipv6 address; use IPv6Address.NONE for 'don't care'
+     * @param switchDPID the switch DPID; use DatapathId.NONE for 'don't care'
+     * @param switchPort the switch port; use OFPort.ZERO for 'don't care'
      * @return an iterator over a set of devices matching the query
-     * @see IDeviceService#queryClassDevices(IEntityClass, Long, 
-     * Short, Integer, Long, Integer)
+     * @see IDeviceService#queryClassDevices(IEntityClass, MacAddress, 
+     * VlanVid, IPv4Address, IPv6Address, DatapathId, OFPort)
      */
-    public Iterator<? extends IDevice> queryDevices(MacAddress macAddress,
+    public Iterator<? extends IDevice> queryDevices(@Nonnull MacAddress macAddress,
                                                     VlanVid vlan,
-                                                    IPv4Address ipv4Address, 
-                                                    DatapathId switchDPID,
-                                                    OFPort switchPort);
+                                                    @Nonnull IPv4Address ipv4Address, 
+                                                    @Nonnull IPv6Address ipv6Address,
+                                                    @Nonnull DatapathId switchDPID,
+                                                    @Nonnull OFPort switchPort);
 
     /**
      * Find devices that match the provided query.  Only the index for
@@ -183,19 +189,21 @@ public interface IDeviceService extends IFloodlightService {
      * of the device list.
      * 
      * @param entityClass The entity class in which to perform the query
-     * @param macAddress The MAC address
-     * @param vlan the VLAN
-     * @param ipv4Address the ipv4 address
-     * @param switchDPID the switch DPID
-     * @param switchPort the switch port
-    * @return an iterator over a set of devices matching the query
-     * @see IDeviceService#queryClassDevices(Long, 
-     * Short, Integer, Long, Integer)
+     * @param macAddress the MAC address; use MacAddress.NONE for 'don't care'
+     * @param vlan the VLAN; use null for 'don't care'; use VlanVid.ZERO for 'untagged'
+     * @param ipv4Address the ipv4 address; use IPv4Address.NONE for 'don't care'
+     * @param ipv6Address the ipv6 address; use IPv6Address.NONE for 'don't care'
+     * @param switchDPID the switch DPID; use DatapathId.NONE for 'don't care'
+     * @param switchPort the switch port; use OFPort.ZERO for 'don't care'
+     * @return an iterator over a set of devices matching the query
+     * @see IDeviceService#queryClassDevices(MacAddress, 
+     * VlanVid, IPv4Address, IPv6Address, DatapathId, OFPort)
      */
     public Iterator<? extends IDevice> queryClassDevices(IEntityClass entityClass,
                                                          MacAddress macAddress,
                                                          VlanVid vlan,
                                                          IPv4Address ipv4Address, 
+                                                         IPv6Address ipv6Address,
                                                          DatapathId switchDPID,
                                                          OFPort switchPort);
     
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/AttachmentPoint.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/AttachmentPoint.java
index 1845ffe8c47a7fbc65cfd50c247aee24e246b3e6..79777f8133e5d82cbf77f720da4037293fadf756 100644
--- a/src/main/java/net/floodlightcontroller/devicemanager/internal/AttachmentPoint.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/AttachmentPoint.java
@@ -125,4 +125,4 @@ public class AttachmentPoint {
                + ", activeSince=" + activeSince + ", lastSeen=" + lastSeen.toString()
                + "]";
     }
-}
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DefaultEntityClassifier.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DefaultEntityClassifier.java
index ea55f8cac2bba0f33746375ff96b40a8ad6825f4..b8d83272254797d19d311985eaec5a80862fbf87 100644
--- a/src/main/java/net/floodlightcontroller/devicemanager/internal/DefaultEntityClassifier.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DefaultEntityClassifier.java
@@ -125,6 +125,5 @@ public class DefaultEntityClassifier implements
     @Override
     public void addListener(IEntityClassListener listener) {
         // no-op
-        
     }
-}
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java
index d751e6fead99131dc9682b6c3fd7268f550bb664..9f028cf749b17434a422b5e2e6a9cc4f46800f6a 100755
--- a/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java
@@ -24,15 +24,18 @@ import java.util.Collections;
 import java.util.Date;
 import java.util.EnumSet;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.TreeSet;
 
 import com.fasterxml.jackson.databind.annotation.JsonSerialize;
 
 import org.projectfloodlight.openflow.types.DatapathId;
 import org.projectfloodlight.openflow.types.IPv4Address;
+import org.projectfloodlight.openflow.types.IPv6Address;
 import org.projectfloodlight.openflow.types.MacAddress;
 import org.projectfloodlight.openflow.types.OFPort;
 import org.projectfloodlight.openflow.types.VlanVid;
@@ -49,690 +52,804 @@ import net.floodlightcontroller.topology.ITopologyService;
 
 /**
  * Concrete implementation of {@link IDevice}
+ * 
  * @author readams
  */
-@JsonSerialize(using=DeviceSerializer.class)
+@JsonSerialize(using = DeviceSerializer.class)
 public class Device implements IDevice {
-    protected static Logger log = LoggerFactory.getLogger(Device.class);
-
-    private final Long deviceKey;
-    protected final DeviceManagerImpl deviceManager;
-
-    protected final Entity[] entities;
-    private final IEntityClass entityClass;
-
-    protected final String macAddressString;
-    // the vlan Ids from the entities of this device
-    protected final VlanVid[] vlanIds;
-    protected volatile String dhcpClientName;
-
-    /**
-     * These are the old attachment points for the device that were
-     * valid no more than INACTIVITY_TIME ago.
-     */
-    protected volatile List<AttachmentPoint> oldAPs;
-    /**
-     * The current attachment points for the device.
-     */
-    protected volatile List<AttachmentPoint> attachmentPoints;
-
-    // ************
-    // Constructors
-    // ************
-
-    /**
-     * Create a device from an entities
-     * @param deviceManager the device manager for this device
-     * @param deviceKey the unique identifier for this device object
-     * @param entity the initial entity for the device
-     * @param entityClass the entity classes associated with the entity
-     */
-    public Device(DeviceManagerImpl deviceManager, Long deviceKey,
-                  Entity entity, IEntityClass entityClass) {
-        this.deviceManager = deviceManager;
-        this.deviceKey = deviceKey;
-        this.entities = new Entity[] {entity};
-        this.macAddressString = entity.getMacAddress().toString();
-        this.entityClass = entityClass;
-        Arrays.sort(this.entities);
-
-        this.dhcpClientName = null;
-        this.oldAPs = null;
-        this.attachmentPoints = null;
-
-        if (entity.getSwitchDPID() != null && entity.getSwitchPort() != null) {
-            DatapathId sw = entity.getSwitchDPID();
-            OFPort port = entity.getSwitchPort();
-
-            if (deviceManager.isValidAttachmentPoint(sw, port)) {
-                AttachmentPoint ap;
-                ap = new AttachmentPoint(sw, port, entity.getLastSeenTimestamp());
-                this.attachmentPoints = new ArrayList<AttachmentPoint>();
-                this.attachmentPoints.add(ap);
-            }
-        }
-        vlanIds = computeVlandIds();
-    }
-
-    /**
-     * Create a device from a set of entities
-     * @param deviceManager the device manager for this device
-     * @param deviceKey the unique identifier for this device object
-     * @param entities the initial entities for the device
-     * @param entityClass the entity class associated with the entities
-     */
-    public Device(DeviceManagerImpl deviceManager,
-                  Long deviceKey,
-                  String dhcpClientName,
-                  Collection<AttachmentPoint> oldAPs,
-                  Collection<AttachmentPoint> attachmentPoints,
-                  Collection<Entity> entities,
-                  IEntityClass entityClass) {
-        this.deviceManager = deviceManager;
-        this.deviceKey = deviceKey;
-        this.dhcpClientName = dhcpClientName;
-        this.entities = entities.toArray(new Entity[entities.size()]);
-        this.oldAPs = null;
-        this.attachmentPoints = null;
-        if (oldAPs != null) {
-            this.oldAPs = new ArrayList<AttachmentPoint>(oldAPs);
-        }
-        if (attachmentPoints != null) {
-            this.attachmentPoints = new ArrayList<AttachmentPoint>(attachmentPoints);
-        }
-        this.macAddressString = this.entities[0].getMacAddress().toString();
-        this.entityClass = entityClass;
-        Arrays.sort(this.entities);
-        vlanIds = computeVlandIds();
-    }
-
-    /**
-     * Construct a new device consisting of the entities from the old device
-     * plus an additional entity.
-     * The caller needs to ensure that the additional entity is not already
-     * present in the array
-     * @param device the old device object
-     * @param newEntity the entity to add. newEntity must be have the same
-     *        entity class as device
-     * @param if positive indicates the index in the entities array were the
-     *        new entity should be inserted. If negative we will compute the
-     *        correct insertion point
-     */
-    public Device(Device device, Entity newEntity, int insertionpoint) {
-        this.deviceManager = device.deviceManager;
-        this.deviceKey = device.deviceKey;
-        this.dhcpClientName = device.dhcpClientName;
-
-        this.entities = new Entity[device.entities.length + 1];
-        if (insertionpoint < 0) {
-            insertionpoint = -(Arrays.binarySearch(device.entities, newEntity) + 1);
-        }
-        if (insertionpoint > 0) {
-            // insertion point is not the beginning:
-            // copy up to insertion point
-            System.arraycopy(device.entities, 0, this.entities, 0, insertionpoint);
-        }
-        if (insertionpoint < device.entities.length) {
-            // insertion point is not the end
-            // copy from insertion point
-            System.arraycopy(device.entities, insertionpoint,
-                             this.entities, insertionpoint + 1,
-                             device.entities.length - insertionpoint);
-        }
-        this.entities[insertionpoint] = newEntity;
-        /*
-        this.entities = Arrays.<Entity>copyOf(device.entities,
-                                              device.entities.length + 1);
-        this.entities[this.entities.length - 1] = newEntity;
-        Arrays.sort(this.entities);
-        */
-        this.oldAPs = null;
-        if (device.oldAPs != null) {
-            this.oldAPs = new ArrayList<AttachmentPoint>(device.oldAPs);
-        }
-        this.attachmentPoints = null;
-        if (device.attachmentPoints != null) {
-            this.attachmentPoints = new ArrayList<AttachmentPoint>(device.attachmentPoints);
-        }
-
-        this.macAddressString = this.entities[0].getMacAddress().toString();
-
-        this.entityClass = device.entityClass;
-        vlanIds = computeVlandIds();
-    }
-
-    private VlanVid[] computeVlandIds() {
-        if (entities.length == 1) {
-            if (entities[0].getVlan() != null) {
-                return new VlanVid[]{ entities[0].getVlan() };
-            } else {
-                return new VlanVid[] { VlanVid.ofVlan(-1) };
-            }
-        }
-
-        TreeSet<VlanVid> vals = new TreeSet<VlanVid>();
-        for (Entity e : entities) {
-            if (e.getVlan() == null)
-                vals.add(VlanVid.ofVlan(-1));
-            else
-                vals.add(e.getVlan());
-        }
-        return vals.toArray(new VlanVid[vals.size()]);
-    }
-
-    /**
-     * Given a list of attachment points (apList), the procedure would return
-     * a map of attachment points for each L2 domain.  L2 domain id is the key.
-     * @param apList
-     * @return
-     */
-    private Map<DatapathId, AttachmentPoint> getAPMap(List<AttachmentPoint> apList) {
-
-        if (apList == null) return null;
-        ITopologyService topology = deviceManager.topology;
-
-        // Get the old attachment points and sort them.
-        List<AttachmentPoint>oldAP = new ArrayList<AttachmentPoint>();
-        if (apList != null) oldAP.addAll(apList);
-
-        // Remove invalid attachment points before sorting.
-        List<AttachmentPoint>tempAP =
-                new ArrayList<AttachmentPoint>();
-        for(AttachmentPoint ap: oldAP) {
-            if (deviceManager.isValidAttachmentPoint(ap.getSw(), ap.getPort())) {
-                tempAP.add(ap);
-            }
-        }
-        oldAP = tempAP;
-
-        Collections.sort(oldAP, deviceManager.apComparator);
-
-        // Map of attachment point by L2 domain Id.
-        Map<DatapathId, AttachmentPoint> apMap = new HashMap<DatapathId, AttachmentPoint>();
-
-        for(int i=0; i<oldAP.size(); ++i) {
-            AttachmentPoint ap = oldAP.get(i);
-            // if this is not a valid attachment point, continue
-            if (!deviceManager.isValidAttachmentPoint(ap.getSw(), ap.getPort()))
-                continue;
-
-            DatapathId id = topology.getL2DomainId(ap.getSw());
-            apMap.put(id, ap);
-        }
-
-        if (apMap.isEmpty()) return null;
-        return apMap;
-    }
-
-    /**
-     * Remove all attachment points that are older than INACTIVITY_INTERVAL
-     * from the list.
-     * @param apList
-     * @return
-     */
-    private boolean removeExpiredAttachmentPoints(List<AttachmentPoint>apList) {
-
-        List<AttachmentPoint> expiredAPs = new ArrayList<AttachmentPoint>();
-
-        if (apList == null) return false;
-
-        for(AttachmentPoint ap: apList) {
-            if (ap.getLastSeen().getTime() + AttachmentPoint.INACTIVITY_INTERVAL < System.currentTimeMillis()) {
-                expiredAPs.add(ap);
-            }
-        }
-        if (expiredAPs.size() > 0) {
-            apList.removeAll(expiredAPs);
-            return true;
-        } else return false;
-    }
-
-    /**
-     * Get a list of duplicate attachment points, given a list of old attachment
-     * points and one attachment point per L2 domain. Given a true attachment
-     * point in the L2 domain, say trueAP, another attachment point in the
-     * same L2 domain, say ap, is duplicate if:
-     * 1. ap is inconsistent with trueAP, and
-     * 2. active time of ap is after that of trueAP; and
-     * 3. last seen time of ap is within the last INACTIVITY_INTERVAL
-     * @param oldAPList
-     * @param apMap
-     * @return
-     */
-    List<AttachmentPoint> getDuplicateAttachmentPoints(List<AttachmentPoint>oldAPList, Map<DatapathId, AttachmentPoint>apMap) {
-        ITopologyService topology = deviceManager.topology;
-        List<AttachmentPoint> dupAPs = new ArrayList<AttachmentPoint>();
-        long timeThreshold = System.currentTimeMillis() - AttachmentPoint.INACTIVITY_INTERVAL;
-
-        if (oldAPList == null || apMap == null)
-            return dupAPs;
-
-        for(AttachmentPoint ap : oldAPList) {
-            DatapathId id = topology.getL2DomainId(ap.getSw());
-            AttachmentPoint trueAP = apMap.get(id);
-
-            if (trueAP == null) continue;
-            boolean c = (topology.isConsistent(trueAP.getSw(), trueAP.getPort(),
-                                              ap.getSw(), ap.getPort()));
-            boolean active = (ap.getActiveSince().after(trueAP.getActiveSince()));
-            boolean last = ap.getLastSeen().getTime() > timeThreshold;
-            if (!c && active && last) {
-                dupAPs.add(ap);
-            }
-        }
-
-        return dupAPs;
-    }
-
-    /**
-     * Update the known attachment points.  This method is called whenever
-     * topology changes. The method returns true if there's any change to
-     * the list of attachment points -- which indicates a possible device
-     * move.
-     * @return
-     */
-    protected boolean updateAttachmentPoint() {
-        boolean moved = false;
-        this.oldAPs = attachmentPoints;
-        if (attachmentPoints == null || attachmentPoints.isEmpty())
-            return false;
-
-        List<AttachmentPoint> apList = new ArrayList<AttachmentPoint>();
-        if (attachmentPoints != null) apList.addAll(attachmentPoints);
-        Map<DatapathId, AttachmentPoint> newMap = getAPMap(apList);
-        if (newMap == null || newMap.size() != apList.size()) {
-            moved = true;
-        }
-
-        // Prepare the new attachment point list.
-        if (moved) {
-            log.info("updateAttachmentPoint: ap {}  newmap {} ", attachmentPoints, newMap);
-            List<AttachmentPoint> newAPList = new ArrayList<AttachmentPoint>();
-            if (newMap != null) newAPList.addAll(newMap.values());
-            this.attachmentPoints = newAPList;
-        }
-
-        // Set the oldAPs to null.
-        return moved;
-    }
-
-    /**
-     * Update the list of attachment points given that a new packet-in
-     * was seen from (sw, port) at time (lastSeen).  The return value is true
-     * if there was any change to the list of attachment points for the device
-     * -- which indicates a device move.
-     * @param sw
-     * @param port
-     * @param lastSeen
-     * @return
-     */
-    protected boolean updateAttachmentPoint(DatapathId sw, OFPort port, Date lastSeen){
-        ITopologyService topology = deviceManager.topology;
-        List<AttachmentPoint> oldAPList;
-        List<AttachmentPoint> apList;
-        boolean oldAPFlag = false;
-
-        if (!deviceManager.isValidAttachmentPoint(sw, port)) return false;
-        AttachmentPoint newAP = new AttachmentPoint(sw, port, lastSeen);
-        //Copy the oldAP and ap list.
-        apList = new ArrayList<AttachmentPoint>();
-        if (attachmentPoints != null) apList.addAll(attachmentPoints);
-        oldAPList = new ArrayList<AttachmentPoint>();
-        if (oldAPs != null) oldAPList.addAll(oldAPs);
-
-        // if the sw, port is in old AP, remove it from there
-        // and update the lastSeen in that object.
-        if (oldAPList.contains(newAP)) {
-            int index = oldAPList.indexOf(newAP);
-            newAP = oldAPList.remove(index);
-            newAP.setLastSeen(lastSeen);
-            this.oldAPs = oldAPList;
-            oldAPFlag = true;
-        }
-
-        // newAP now contains the new attachment point.
-
-        // Get the APMap is null or empty.
-        Map<DatapathId, AttachmentPoint> apMap = getAPMap(apList);
-        if (apMap == null || apMap.isEmpty()) {
-            apList.add(newAP);
-            attachmentPoints = apList;
-            // there are no old attachment points - since the device exists, this
-            // may be because the host really moved (so the old AP port went down);
-            // or it may be because the switch restarted (so old APs were nullified).
-            // For now we will treat both cases as host moved.
-            return true;
-        }
-
-        DatapathId id = topology.getL2DomainId(sw);
-        AttachmentPoint oldAP = apMap.get(id);
-
-        if (oldAP == null) { // No attachment on this L2 domain.
-            apList = new ArrayList<AttachmentPoint>();
-            apList.addAll(apMap.values());
-            apList.add(newAP);
-            this.attachmentPoints = apList;
-            return true; // new AP found on an L2 island.
-        }
-
-        // There is already a known attachment point on the same L2 island.
-        // we need to compare oldAP and newAP.
-        if (oldAP.equals(newAP)) {
-            // nothing to do here. just the last seen has to be changed.
-            if (newAP.lastSeen.after(oldAP.lastSeen)) {
-                oldAP.setLastSeen(newAP.lastSeen);
-            }
-            this.attachmentPoints =
-                    new ArrayList<AttachmentPoint>(apMap.values());
-            return false; // nothing to do here.
-        }
-
-        int x = deviceManager.apComparator.compare(oldAP, newAP);
-        if (x < 0) {
-            // newAP replaces oldAP.
-            apMap.put(id, newAP);
-            this.attachmentPoints =
-                    new ArrayList<AttachmentPoint>(apMap.values());
-
-            oldAPList = new ArrayList<AttachmentPoint>();
-            if (oldAPs != null) oldAPList.addAll(oldAPs);
-            oldAPList.add(oldAP);
-            this.oldAPs = oldAPList;
-            if (!topology.isInSameBroadcastDomain(oldAP.getSw(), oldAP.getPort(),
-                                                  newAP.getSw(), newAP.getPort()))
-                return true; // attachment point changed.
-        } else  if (oldAPFlag) {
-            // retain oldAP  as is.  Put the newAP in oldAPs for flagging
-            // possible duplicates.
-                oldAPList = new ArrayList<AttachmentPoint>();
-                if (oldAPs != null) oldAPList.addAll(oldAPs);
-                // Add ot oldAPList only if it was picked up from the oldAPList
-                oldAPList.add(newAP);
-                this.oldAPs = oldAPList;
-        }
-        return false;
-    }
-
-    /**
-     * Delete (sw,port) from the list of list of attachment points
-     * and oldAPs.
-     * @param sw
-     * @param port
-     * @return
-     */
-    public boolean deleteAttachmentPoint(DatapathId sw, OFPort port) {
-        AttachmentPoint ap = new AttachmentPoint(sw, port, new Date(0));
-        
-        if (this.oldAPs != null) {
-            ArrayList<AttachmentPoint> apList = new ArrayList<AttachmentPoint>();
-            apList.addAll(this.oldAPs);
-            int index = apList.indexOf(ap);
-            if (index > 0) {
-                apList.remove(index);
-                this.oldAPs = apList;
-            }
-        }
-
-        if (this.attachmentPoints != null) {
-            ArrayList<AttachmentPoint> apList = new ArrayList<AttachmentPoint>();
-            apList.addAll(this.attachmentPoints);
-            int index = apList.indexOf(ap);
-            if (index > 0) {
-                apList.remove(index);
-                this.attachmentPoints = apList;
-                return true;
-            }
-        }
-        return false;
-    }
-
-    public boolean deleteAttachmentPoint(DatapathId sw) {
-        boolean deletedFlag;
-        ArrayList<AttachmentPoint> apList;
-        ArrayList<AttachmentPoint> modifiedList;
-
-        // Delete the APs on switch sw in oldAPs.
-        deletedFlag = false;
-        apList = new ArrayList<AttachmentPoint>();
-        if (this.oldAPs != null)
-            apList.addAll(this.oldAPs);
-        modifiedList = new ArrayList<AttachmentPoint>();
-
-        for(AttachmentPoint ap: apList) {
-            if (ap.getSw().equals(sw)) {
-                deletedFlag = true;
-            } else {
-                modifiedList.add(ap);
-            }
-        }
-
-        if (deletedFlag) {
-            this.oldAPs = modifiedList;
-        }
-
-        // Delete the APs on switch sw in attachmentPoints.
-        deletedFlag = false;
-        apList = new ArrayList<AttachmentPoint>();
-        if (this.attachmentPoints != null)
-            apList.addAll(this.attachmentPoints);
-        modifiedList = new ArrayList<AttachmentPoint>();
-
-        for(AttachmentPoint ap: apList) {
-            if (ap.getSw().equals(sw)) {
-                deletedFlag = true;
-            } else {
-                modifiedList.add(ap);
-            }
-        }
-
-        if (deletedFlag) {
-            this.attachmentPoints = modifiedList;
-            return true;
-        }
-
-        return false;
-    }
-
-    // *******
-    // IDevice
-    // *******
-
-    @Override
-    public SwitchPort[] getOldAP() {
-        List<SwitchPort> sp = new ArrayList<SwitchPort>();
-        SwitchPort [] returnSwitchPorts = new SwitchPort[] {};
-        if (oldAPs == null) return returnSwitchPorts;
-        if (oldAPs.isEmpty()) return returnSwitchPorts;
-
-        // copy ap list.
-        List<AttachmentPoint> oldAPList;
-        oldAPList = new ArrayList<AttachmentPoint>();
-
-        if (oldAPs != null) oldAPList.addAll(oldAPs);
-        removeExpiredAttachmentPoints(oldAPList);
-
-        if (oldAPList != null) {
-            for(AttachmentPoint ap: oldAPList) {
-                SwitchPort swport = new SwitchPort(ap.getSw(),
-                                                   ap.getPort());
-                    sp.add(swport);
-            }
-        }
-        return sp.toArray(new SwitchPort[sp.size()]);
-    }
-
-    @Override
-    public SwitchPort[] getAttachmentPoints() {
-        return getAttachmentPoints(false);
-    }
-
-    @Override
-    public SwitchPort[] getAttachmentPoints(boolean includeError) {
-        List<SwitchPort> sp = new ArrayList<SwitchPort>();
-        SwitchPort [] returnSwitchPorts = new SwitchPort[] {};
-        if (attachmentPoints == null) return returnSwitchPorts;
-        if (attachmentPoints.isEmpty()) return returnSwitchPorts;
-
-        // copy ap list.
-        List<AttachmentPoint> apList = attachmentPoints;
-
-        if (apList != null) {
-            for(AttachmentPoint ap: apList) {
-                SwitchPort swport = new SwitchPort(ap.getSw(),
-                                                   ap.getPort());
-                    sp.add(swport);
-            }
-        }
-
-        if (!includeError)
-            return sp.toArray(new SwitchPort[sp.size()]);
-
-        List<AttachmentPoint> oldAPList;
-        oldAPList = new ArrayList<AttachmentPoint>();
-
-        if (oldAPs != null) oldAPList.addAll(oldAPs);
-
-        if (removeExpiredAttachmentPoints(oldAPList))
-            this.oldAPs = oldAPList;
-
-        List<AttachmentPoint> dupList;
-        // get AP map.
-        Map<DatapathId, AttachmentPoint> apMap = getAPMap(apList);
-        dupList = this.getDuplicateAttachmentPoints(oldAPList, apMap);
-        if (dupList != null) {
-            for(AttachmentPoint ap: dupList) {
-                SwitchPort swport = new SwitchPort(ap.getSw(),
-                                                   ap.getPort(),
-                                                   ErrorStatus.DUPLICATE_DEVICE);
-                    sp.add(swport);
-            }
-        }
-        return sp.toArray(new SwitchPort[sp.size()]);
-    }
-
-    @Override
-    public Long getDeviceKey() {
-        return deviceKey;
-    }
-
-    @Override
-    public MacAddress getMACAddress() {
-        // we assume only one MAC per device for now.
-        return entities[0].getMacAddress();
-    }
-
-    @Override
-    public String getMACAddressString() {
-        return macAddressString;
-    }
-
-    @Override
-    public VlanVid[] getVlanId() {
-        return Arrays.copyOf(vlanIds, vlanIds.length);
-    }
-
-    static final EnumSet<DeviceField> ipv4Fields = EnumSet.of(DeviceField.IPV4);
-
-    @Override
-    public IPv4Address[] getIPv4Addresses() {
-        // XXX - TODO we can cache this result.  Let's find out if this
-        // is really a performance bottleneck first though.
-
-        TreeSet<IPv4Address> vals = new TreeSet<IPv4Address>();
-        for (Entity e : entities) {
-            if (e.getIpv4Address() == null) continue;
-
-            // We have an IP address only if among the devices within the class
-            // we have the most recent entity with that IP.
-            boolean validIP = true;
-            Iterator<Device> devices =
-                    deviceManager.queryClassByEntity(entityClass, ipv4Fields, e);
-            while (devices.hasNext()) {
-                Device d = devices.next();
-                if (deviceKey.equals(d.getDeviceKey()))
-                    continue;
-                for (Entity se : d.entities) {
-                    if (se.getIpv4Address() != null &&
-                            se.getIpv4Address().equals(e.getIpv4Address()) &&
-                            se.getLastSeenTimestamp() != null &&
-                            0 < se.getLastSeenTimestamp().
-                            compareTo(e.getLastSeenTimestamp())) {
-                        validIP = false;
-                        break;
-                    }
-                }
-                if (!validIP)
-                    break;
-            }
-
-            if (validIP)
-                vals.add(e.getIpv4Address());
-        }
-
-        return vals.toArray(new IPv4Address[vals.size()]);
-    }
-
-    @Override
-    public VlanVid[] getSwitchPortVlanIds(SwitchPort swp) {
-        TreeSet<VlanVid> vals = new TreeSet<VlanVid>();
-        for (Entity e : entities) {
-            if (e.switchDPID.equals(swp.getSwitchDPID())
-                    && e.switchPort.equals(swp.getPort())) {
-                if (e.getVlan() == null)
-                    vals.add(VlanVid.ofVlan(-1)); //TODO Update all -1 VLANs (untagged) to the new VlanVid.ZERO
-                else
-                    vals.add(e.getVlan());
-            }
-        }
-        return vals.toArray(new VlanVid[vals.size()]);
-    }
-
-    @Override
-    public Date getLastSeen() {
-        Date d = null;
-        for (int i = 0; i < entities.length; i++) {
-            if (d == null ||
-                    entities[i].getLastSeenTimestamp().compareTo(d) > 0)
-                d = entities[i].getLastSeenTimestamp();
-        }
-        return d;
-    }
-
-    // ***************
-    // Getters/Setters
-    // ***************
-
-    @Override
-    public IEntityClass getEntityClass() {
-        return entityClass;
-    }
-
-    public Entity[] getEntities() {
-        return entities;
-    }
-
-    public String getDHCPClientName() {
-        return dhcpClientName;
-    }
-
-    // ***************
-    // Utility Methods
-    // ***************
-
-    /**
-     * Check whether the device contains the specified entity
-     * @param entity the entity to search for
-     * @return the index of the entity, or <0 if not found
-     */
-    protected int entityIndex(Entity entity) {
-        return Arrays.binarySearch(entities, entity);
-    }
-
-    // ******
-    // Object
-    // ******
-
-    @Override
+	protected static Logger log = LoggerFactory.getLogger(Device.class);
+
+	private final Long deviceKey;
+	protected final DeviceManagerImpl deviceManager;
+
+	protected final Entity[] entities;
+	private final IEntityClass entityClass;
+
+	protected final String macAddressString;
+	// the vlan Ids from the entities of this device
+	protected final VlanVid[] vlanIds;
+	protected volatile String dhcpClientName;
+
+	/**
+	 * These are the old attachment points for the device that were valid no
+	 * more than INACTIVITY_TIME ago.
+	 */
+	protected volatile List<AttachmentPoint> oldAPs;
+	/**
+	 * The current attachment points for the device.
+	 */
+	protected volatile List<AttachmentPoint> attachmentPoints;
+
+	// ************
+	// Constructors
+	// ************
+
+	/**
+	 * Create a device from an entities
+	 * 
+	 * @param deviceManager
+	 *            the device manager for this device
+	 * @param deviceKey
+	 *            the unique identifier for this device object
+	 * @param entity
+	 *            the initial entity for the device
+	 * @param entityClass
+	 *            the entity classes associated with the entity
+	 */
+	public Device(DeviceManagerImpl deviceManager, Long deviceKey,
+			Entity entity, IEntityClass entityClass) {
+		this.deviceManager = deviceManager;
+		this.deviceKey = deviceKey;
+		this.entities = new Entity[] { entity };
+		this.macAddressString = entity.getMacAddress().toString();
+		this.entityClass = entityClass;
+		Arrays.sort(this.entities);
+
+		this.dhcpClientName = null;
+		this.oldAPs = null;
+		this.attachmentPoints = null;
+
+		if (!entity.getSwitchDPID().equals(DatapathId.NONE)
+				&& !entity.getSwitchPort().equals(OFPort.ZERO)) {
+			DatapathId sw = entity.getSwitchDPID();
+			OFPort port = entity.getSwitchPort();
+
+			if (deviceManager.isValidAttachmentPoint(sw, port)) {
+				AttachmentPoint ap;
+				ap = new AttachmentPoint(sw, port,
+						entity.getLastSeenTimestamp());
+				this.attachmentPoints = new ArrayList<AttachmentPoint>();
+				this.attachmentPoints.add(ap);
+			}
+		}
+		vlanIds = computeVlandIds();
+	}
+
+	/**
+	 * Create a device from a set of entities
+	 * 
+	 * @param deviceManager
+	 *            the device manager for this device
+	 * @param deviceKey
+	 *            the unique identifier for this device object
+	 * @param entities
+	 *            the initial entities for the device
+	 * @param entityClass
+	 *            the entity class associated with the entities
+	 */
+	public Device(DeviceManagerImpl deviceManager, Long deviceKey,
+			String dhcpClientName, Collection<AttachmentPoint> oldAPs,
+			Collection<AttachmentPoint> attachmentPoints,
+			Collection<Entity> entities, IEntityClass entityClass) {
+		this.deviceManager = deviceManager;
+		this.deviceKey = deviceKey;
+		this.dhcpClientName = dhcpClientName;
+		this.entities = entities.toArray(new Entity[entities.size()]);
+		this.oldAPs = null;
+		this.attachmentPoints = null;
+		if (oldAPs != null) {
+			this.oldAPs = new ArrayList<AttachmentPoint>(oldAPs);
+		}
+		if (attachmentPoints != null) {
+			this.attachmentPoints = new ArrayList<AttachmentPoint>(
+					attachmentPoints);
+		}
+		this.macAddressString = this.entities[0].getMacAddress().toString();
+		this.entityClass = entityClass;
+		Arrays.sort(this.entities);
+		vlanIds = computeVlandIds();
+	}
+
+	/**
+	 * Construct a new device consisting of the entities from the old device
+	 * plus an additional entity. The caller needs to ensure that the additional
+	 * entity is not already present in the array
+	 * 
+	 * @param device
+	 *            the old device object
+	 * @param newEntity
+	 *            the entity to add. newEntity must be have the same entity
+	 *            class as device
+	 * @param if positive indicates the index in the entities array were the new
+	 *        entity should be inserted. If negative we will compute the correct
+	 *        insertion point
+	 */
+	public Device(Device device, Entity newEntity, int insertionpoint) {
+		this.deviceManager = device.deviceManager;
+		this.deviceKey = device.deviceKey;
+		this.dhcpClientName = device.dhcpClientName;
+
+		if (insertionpoint < 0) {
+			insertionpoint = -(Arrays.binarySearch(device.entities, newEntity) + 1);
+		}
+		
+		/*
+		 * If insertion point is still negative, then the entity exists already.
+		 * Replace it with the new entity.
+		 */
+		if (insertionpoint < 0) {
+			log.warn("Performing a replacement upon new entity add in Device. Should the entity have been removed first?");
+			this.entities = new Entity[device.entities.length];
+			int replacementpoint = -insertionpoint - 1; /* get the original binarySearch return (the positive 0-relative position) */
+			System.arraycopy(device.entities, 0, this.entities, 0, replacementpoint); /* 0 to replacementpoint-1 (or replacementpoint replacements) */
+			this.entities[replacementpoint] = newEntity;
+			System.arraycopy(device.entities, replacementpoint + 1, this.entities, replacementpoint + 1, device.entities.length - (replacementpoint + 1));
+		} else {
+			this.entities = new Entity[device.entities.length + 1];
+			if (insertionpoint > 0) {
+				// insertion point is not the beginning:
+				// copy up to insertion point
+				System.arraycopy(device.entities, 0, this.entities, 0,
+						insertionpoint);
+			}
+			if (insertionpoint < device.entities.length) {
+				// insertion point is not the end
+				// copy from insertion point
+				System.arraycopy(device.entities, insertionpoint, this.entities,
+						insertionpoint + 1, device.entities.length - insertionpoint);
+			}
+			this.entities[insertionpoint] = newEntity;
+		}
+		/*
+		 * this.entities = Arrays.<Entity>copyOf(device.entities,
+		 * device.entities.length + 1); this.entities[this.entities.length - 1]
+		 * = newEntity; Arrays.sort(this.entities);
+		 */
+		this.oldAPs = null;
+		if (device.oldAPs != null) {
+			this.oldAPs = new ArrayList<AttachmentPoint>(device.oldAPs);
+		}
+		this.attachmentPoints = null;
+		if (device.attachmentPoints != null) {
+			this.attachmentPoints = new ArrayList<AttachmentPoint>(
+					device.attachmentPoints);
+		}
+
+		this.macAddressString = this.entities[0].getMacAddress().toString();
+
+		this.entityClass = device.entityClass;
+		vlanIds = computeVlandIds();
+	}
+
+	private VlanVid[] computeVlandIds() {
+		if (entities.length == 1) {
+			return new VlanVid[] { entities[0].getVlan() };
+		}
+
+		TreeSet<VlanVid> vals = new TreeSet<VlanVid>();
+		for (Entity e : entities) {
+			vals.add(e.getVlan());
+		}
+		return vals.toArray(new VlanVid[vals.size()]);
+	}
+
+	/**
+	 * Given a list of attachment points (apList), the procedure would return a
+	 * map of attachment points for each L2 domain. L2 domain id is the key.
+	 * 
+	 * @param apList
+	 * @return
+	 */
+	private Map<DatapathId, AttachmentPoint> getAPMap(
+			List<AttachmentPoint> apList) {
+
+		if (apList == null)
+			return null;
+
+		// Get the old attachment points and sort them.
+		List<AttachmentPoint> oldAP = new ArrayList<AttachmentPoint>();
+		if (apList != null) {
+			oldAP.addAll(apList);
+		}
+
+		// Remove invalid attachment points before sorting.
+		List<AttachmentPoint> tempAP = new ArrayList<AttachmentPoint>();
+		for (AttachmentPoint ap : oldAP) {
+			if (deviceManager.isValidAttachmentPoint(ap.getSw(), ap.getPort())) {
+				tempAP.add(ap);
+			}
+		}
+		oldAP = tempAP;
+
+		Collections.sort(oldAP, deviceManager.apComparator);
+
+		// Map of attachment point by L2 domain Id.
+		Map<DatapathId, AttachmentPoint> apMap = new HashMap<DatapathId, AttachmentPoint>();
+
+		for (int i = 0; i < oldAP.size(); ++i) {
+			AttachmentPoint ap = oldAP.get(i);
+			// if this is not a valid attachment point, continue
+			if (!deviceManager.isValidAttachmentPoint(ap.getSw(), ap.getPort()))
+				continue;
+
+			DatapathId id = deviceManager.topology.getL2DomainId(ap.getSw());
+			apMap.put(id, ap);
+		}
+
+		if (apMap.isEmpty())
+			return null;
+		return apMap;
+	}
+
+	/**
+	 * Remove all attachment points that are older than INACTIVITY_INTERVAL from
+	 * the list.
+	 * 
+	 * @param apList
+	 * @return
+	 */
+	private boolean removeExpiredAttachmentPoints(List<AttachmentPoint> apList) {
+
+		List<AttachmentPoint> expiredAPs = new ArrayList<AttachmentPoint>();
+
+		if (apList == null)
+			return false;
+
+		for (AttachmentPoint ap : apList) {
+			if (ap.getLastSeen().getTime()
+					+ AttachmentPoint.INACTIVITY_INTERVAL < System
+						.currentTimeMillis()) {
+				expiredAPs.add(ap);
+			}
+		}
+		if (expiredAPs.size() > 0) {
+			apList.removeAll(expiredAPs);
+			return true;
+		} else
+			return false;
+	}
+
+	/**
+	 * Get a list of duplicate attachment points, given a list of old attachment
+	 * points and one attachment point per L2 domain. Given a true attachment
+	 * point in the L2 domain, say trueAP, another attachment point in the same
+	 * L2 domain, say ap, is duplicate if: 1. ap is inconsistent with trueAP,
+	 * and 2. active time of ap is after that of trueAP; and 3. last seen time
+	 * of ap is within the last INACTIVITY_INTERVAL
+	 * 
+	 * @param oldAPList
+	 * @param apMap
+	 * @return
+	 */
+	List<AttachmentPoint> getDuplicateAttachmentPoints(
+			List<AttachmentPoint> oldAPList,
+			Map<DatapathId, AttachmentPoint> apMap) {
+		ITopologyService topology = deviceManager.topology;
+		List<AttachmentPoint> dupAPs = new ArrayList<AttachmentPoint>();
+		long timeThreshold = System.currentTimeMillis() - AttachmentPoint.INACTIVITY_INTERVAL;
+
+		if (oldAPList == null || apMap == null) {
+			return dupAPs;
+		}
+		
+		Set<DatapathId> visitedIslands = new HashSet<DatapathId>();
+			
+		for (AttachmentPoint ap : oldAPList) {
+			DatapathId id = topology.getL2DomainId(ap.getSw());
+			AttachmentPoint trueAP = apMap.get(id);
+
+			if (trueAP == null) {
+				continue;
+			}
+			boolean c = (topology.isConsistent(trueAP.getSw(),
+					trueAP.getPort(), ap.getSw(), ap.getPort()));
+			boolean active = trueAP.getActiveSince().after(ap.getActiveSince())
+					&& ap.getLastSeen().after(trueAP.getLastSeen());
+			boolean last = ap.getLastSeen().getTime() > timeThreshold;
+			if (!c && active && last) {
+				visitedIslands.add(id);
+			}
+		}
+		
+		/*
+		 * Now that we've found the islands with a duplicate,
+		 * go back and for every AP on those islands that
+		 * has not expired, add them as duplicates to the list.
+		 */
+		for (AttachmentPoint ap : oldAPList) {				
+			DatapathId id = topology.getL2DomainId(ap.getSw());
+			if (visitedIslands.contains(id)) {
+				if (ap.getLastSeen().getTime() > timeThreshold) {
+					dupAPs.add(ap);
+				}
+			}
+		}
+
+		return dupAPs;
+	}
+
+	/**
+	 * Update the known attachment points. This method is called whenever
+	 * topology changes. The method returns true if there's any change to the
+	 * list of attachment points -- which indicates a possible device move.
+	 * 
+	 * @return
+	 */
+	protected boolean updateAttachmentPoint() {
+		boolean moved = false;
+		this.oldAPs = attachmentPoints;
+		if (attachmentPoints == null || attachmentPoints.isEmpty()) {
+			return false;
+		}
+
+		List<AttachmentPoint> apList = new ArrayList<AttachmentPoint>();
+		if (attachmentPoints != null) {
+			apList.addAll(attachmentPoints);
+		}
+		Map<DatapathId, AttachmentPoint> newMap = getAPMap(apList);
+		if (newMap == null || newMap.size() != apList.size()) {
+			moved = true;
+		}
+
+		// Prepare the new attachment point list
+		if (moved) {
+			log.info("updateAttachmentPoint: ap {}  newmap {} ",
+					attachmentPoints, newMap);
+			List<AttachmentPoint> newAPList = new ArrayList<AttachmentPoint>();
+			if (newMap != null) {
+				newAPList.addAll(newMap.values());
+			}
+			this.attachmentPoints = newAPList;
+		}
+
+		// Set the oldAPs to null.
+		return moved;
+	}
+
+	/**
+	 * Update the list of attachment points given that a new packet-in was seen
+	 * from (sw, port) at time (lastSeen). The return value is true if there was
+	 * any change to the list of attachment points for the device -- which
+	 * indicates a device move.
+	 * 
+	 * @param sw
+	 * @param port
+	 * @param lastSeen
+	 * @return
+	 */
+	protected boolean updateAttachmentPoint(DatapathId sw, OFPort port,
+			Date lastSeen) {
+		ITopologyService topology = deviceManager.topology;
+		List<AttachmentPoint> oldAPList;
+		List<AttachmentPoint> apList;
+		boolean oldAPFlag = false;
+
+		if (!deviceManager.isValidAttachmentPoint(sw, port))
+			return false;
+		AttachmentPoint newAP = new AttachmentPoint(sw, port, lastSeen);
+		// Copy the oldAP and ap list.
+		apList = new ArrayList<AttachmentPoint>();
+		if (attachmentPoints != null)
+			apList.addAll(attachmentPoints);
+		oldAPList = new ArrayList<AttachmentPoint>();
+		if (oldAPs != null)
+			oldAPList.addAll(oldAPs);
+
+		// if the sw, port is in old AP, remove it from there
+		// and update the lastSeen in that object.
+		if (oldAPList.contains(newAP)) {
+			int index = oldAPList.indexOf(newAP);
+			newAP = oldAPList.remove(index);
+			newAP.setLastSeen(lastSeen);
+			this.oldAPs = oldAPList;
+			oldAPFlag = true;
+		}
+
+		// newAP now contains the new attachment point.
+
+		// Get the APMap is null or empty.
+		Map<DatapathId, AttachmentPoint> apMap = getAPMap(apList);
+		if (apMap == null || apMap.isEmpty()) {
+			apList.add(newAP);
+			attachmentPoints = apList;
+			// there are no old attachment points - since the device exists,
+			// this
+			// may be because the host really moved (so the old AP port went
+			// down);
+			// or it may be because the switch restarted (so old APs were
+			// nullified).
+			// For now we will treat both cases as host moved.
+			return true;
+		}
+
+		DatapathId id = topology.getL2DomainId(sw);
+		AttachmentPoint oldAP = apMap.get(id);
+
+		if (oldAP == null) { // No attachment on this L2 domain.
+			apList = new ArrayList<AttachmentPoint>();
+			apList.addAll(apMap.values());
+			apList.add(newAP);
+			this.attachmentPoints = apList;
+			return true; // new AP found on an L2 island.
+		}
+
+		// There is already a known attachment point on the same L2 island.
+		// we need to compare oldAP and newAP.
+		if (oldAP.equals(newAP)) {
+			// nothing to do here. just the last seen has to be changed.
+			if (newAP.lastSeen.after(oldAP.lastSeen)) {
+				oldAP.setLastSeen(newAP.lastSeen);
+			}
+			this.attachmentPoints = new ArrayList<AttachmentPoint>(
+					apMap.values());
+			return false; // nothing to do here.
+		}
+
+		int x = deviceManager.apComparator.compare(oldAP, newAP);
+		if (x < 0) {
+			// newAP replaces oldAP.
+			apMap.put(id, newAP);
+			this.attachmentPoints = new ArrayList<AttachmentPoint>(
+					apMap.values());
+
+			oldAPList = new ArrayList<AttachmentPoint>();
+			if (oldAPs != null)
+				oldAPList.addAll(oldAPs);
+			oldAPList.add(oldAP);
+			this.oldAPs = oldAPList;
+			if (!topology.isInSameBroadcastDomain(oldAP.getSw(),
+					oldAP.getPort(), newAP.getSw(), newAP.getPort()))
+				return true; // attachment point changed.
+		} else if (oldAPFlag) {
+			// retain oldAP as is. Put the newAP in oldAPs for flagging
+			// possible duplicates.
+			oldAPList = new ArrayList<AttachmentPoint>();
+			if (oldAPs != null)
+				oldAPList.addAll(oldAPs);
+			// Add to oldAPList only if it was picked up from the oldAPList
+			oldAPList.add(newAP);
+			this.oldAPs = oldAPList;
+		}
+		return false;
+	}
+
+	/**
+	 * Delete (sw,port) from the list of list of attachment points and oldAPs.
+	 * 
+	 * @param sw
+	 * @param port
+	 * @return
+	 */
+	public boolean deleteAttachmentPoint(DatapathId sw, OFPort port) {
+		AttachmentPoint ap = new AttachmentPoint(sw, port, new Date(0));
+
+		if (this.oldAPs != null) {
+			ArrayList<AttachmentPoint> apList = new ArrayList<AttachmentPoint>();
+			apList.addAll(this.oldAPs);
+			int index = apList.indexOf(ap);
+			if (index > 0) {
+				apList.remove(index);
+				this.oldAPs = apList;
+			}
+		}
+
+		if (this.attachmentPoints != null) {
+			ArrayList<AttachmentPoint> apList = new ArrayList<AttachmentPoint>();
+			apList.addAll(this.attachmentPoints);
+			int index = apList.indexOf(ap);
+			if (index > 0) {
+				apList.remove(index);
+				this.attachmentPoints = apList;
+				return true;
+			}
+		}
+		return false;
+	}
+
+	public boolean deleteAttachmentPoint(DatapathId sw) {
+		boolean deletedFlag;
+		ArrayList<AttachmentPoint> apList;
+		ArrayList<AttachmentPoint> modifiedList;
+
+		// Delete the APs on switch sw in oldAPs.
+		deletedFlag = false;
+		apList = new ArrayList<AttachmentPoint>();
+		if (this.oldAPs != null)
+			apList.addAll(this.oldAPs);
+		modifiedList = new ArrayList<AttachmentPoint>();
+
+		for (AttachmentPoint ap : apList) {
+			if (ap.getSw().equals(sw)) {
+				deletedFlag = true;
+			} else {
+				modifiedList.add(ap);
+			}
+		}
+
+		if (deletedFlag) {
+			this.oldAPs = modifiedList;
+		}
+
+		// Delete the APs on switch sw in attachmentPoints.
+		deletedFlag = false;
+		apList = new ArrayList<AttachmentPoint>();
+		if (this.attachmentPoints != null)
+			apList.addAll(this.attachmentPoints);
+		modifiedList = new ArrayList<AttachmentPoint>();
+
+		for (AttachmentPoint ap : apList) {
+			if (ap.getSw().equals(sw)) {
+				deletedFlag = true;
+			} else {
+				modifiedList.add(ap);
+			}
+		}
+
+		if (deletedFlag) {
+			this.attachmentPoints = modifiedList;
+			return true;
+		}
+
+		return false;
+	}
+
+	// *******
+	// IDevice
+	// *******
+
+	@Override
+	public SwitchPort[] getOldAP() {
+		List<SwitchPort> sp = new ArrayList<SwitchPort>();
+		SwitchPort[] returnSwitchPorts = new SwitchPort[] {};
+		if (oldAPs == null)
+			return returnSwitchPorts;
+		if (oldAPs.isEmpty())
+			return returnSwitchPorts;
+
+		// copy ap list.
+		List<AttachmentPoint> oldAPList;
+		oldAPList = new ArrayList<AttachmentPoint>();
+
+		if (oldAPs != null)
+			oldAPList.addAll(oldAPs);
+		removeExpiredAttachmentPoints(oldAPList);
+
+		if (oldAPList != null) {
+			for (AttachmentPoint ap : oldAPList) {
+				SwitchPort swport = new SwitchPort(ap.getSw(), ap.getPort());
+				sp.add(swport);
+			}
+		}
+		return sp.toArray(new SwitchPort[sp.size()]);
+	}
+
+	@Override
+	public SwitchPort[] getAttachmentPoints() {
+		return getAttachmentPoints(false);
+	}
+
+	@Override
+	public SwitchPort[] getAttachmentPoints(boolean includeError) {
+		List<SwitchPort> sp = new ArrayList<SwitchPort>();
+		SwitchPort[] returnSwitchPorts = new SwitchPort[] {};
+		if (attachmentPoints == null)
+			return returnSwitchPorts;
+		if (attachmentPoints.isEmpty())
+			return returnSwitchPorts;
+
+		// copy ap list.
+		List<AttachmentPoint> apList = new ArrayList<AttachmentPoint>(
+				attachmentPoints);
+
+		if (apList != null) {
+			for (AttachmentPoint ap : apList) {
+				SwitchPort swport = new SwitchPort(ap.getSw(), ap.getPort());
+				sp.add(swport);
+			}
+		}
+
+		if (!includeError)
+			return sp.toArray(new SwitchPort[sp.size()]);
+
+		List<AttachmentPoint> oldAPList;
+		oldAPList = new ArrayList<AttachmentPoint>();
+
+		if (oldAPs != null)
+			oldAPList.addAll(oldAPs);
+
+		if (removeExpiredAttachmentPoints(oldAPList))
+			this.oldAPs = oldAPList;
+
+		List<AttachmentPoint> dupList;
+		// get AP map.
+		Map<DatapathId, AttachmentPoint> apMap = getAPMap(apList);
+		dupList = this.getDuplicateAttachmentPoints(oldAPList, apMap);
+		if (dupList != null) {
+			for (AttachmentPoint ap : dupList) {
+				SwitchPort swport = new SwitchPort(ap.getSw(), ap.getPort(),
+						ErrorStatus.DUPLICATE_DEVICE);
+				sp.add(swport);
+			}
+		}
+		return sp.toArray(new SwitchPort[sp.size()]);
+	}
+
+	@Override
+	public Long getDeviceKey() {
+		return deviceKey;
+	}
+
+	@Override
+	public MacAddress getMACAddress() {
+		// we assume only one MAC per device for now.
+		return entities[0].getMacAddress();
+	}
+
+	@Override
+	public String getMACAddressString() {
+		return macAddressString;
+	}
+
+	@Override
+	public VlanVid[] getVlanId() {
+		return Arrays.copyOf(vlanIds, vlanIds.length);
+	}
+
+	static final EnumSet<DeviceField> ipv4Fields = EnumSet.of(DeviceField.IPv4);
+	static final EnumSet<DeviceField> ipv6Fields = EnumSet.of(DeviceField.IPv6);
+
+	@Override
+	public IPv4Address[] getIPv4Addresses() {
+		// XXX - TODO we can cache this result. Let's find out if this
+		// is really a performance bottleneck first though.
+
+		TreeSet<IPv4Address> vals = new TreeSet<IPv4Address>();
+		for (Entity e : entities) {
+			if (e.getIpv4Address().equals(IPv4Address.NONE))
+				continue;
+
+			// We have an IP address only if among the devices within the class
+			// we have the most recent entity with that IP.
+			boolean validIP = true;
+			Iterator<Device> devices = deviceManager.queryClassByEntity(
+					entityClass, ipv4Fields, e);
+			while (devices.hasNext()) {
+				Device d = devices.next();
+				if (deviceKey.equals(d.getDeviceKey()))
+					continue;
+				for (Entity se : d.entities) {
+					if (se.getIpv4Address() != null
+							&& se.getIpv4Address().equals(e.getIpv4Address())
+							&& !se.getLastSeenTimestamp()
+									.equals(Entity.NO_DATE)
+							&& 0 < se.getLastSeenTimestamp().compareTo(
+									e.getLastSeenTimestamp())) {
+						validIP = false;
+						break;
+					}
+				}
+				if (!validIP)
+					break;
+			}
+
+			if (validIP)
+				vals.add(e.getIpv4Address());
+		}
+
+		return vals.toArray(new IPv4Address[vals.size()]);
+	}
+
+	@Override
+	public IPv6Address[] getIPv6Addresses() {
+		TreeSet<IPv6Address> vals = new TreeSet<IPv6Address>();
+		for (Entity e : entities) {
+			if (e.getIpv6Address().equals(IPv6Address.NONE))
+				continue;
+
+			// We have an IP address only if among the devices within the class
+			// we have the most recent entity with that IP.
+			boolean validIP = true;
+			Iterator<Device> devices = deviceManager.queryClassByEntity(
+					entityClass, ipv6Fields, e);
+			while (devices.hasNext()) {
+				Device d = devices.next();
+				if (deviceKey.equals(d.getDeviceKey()))
+					continue;
+				for (Entity se : d.entities) {
+					if (se.getIpv6Address() != null
+							&& se.getIpv6Address().equals(e.getIpv6Address())
+							&& !se.getLastSeenTimestamp()
+									.equals(Entity.NO_DATE)
+							&& 0 < se.getLastSeenTimestamp().compareTo(
+									e.getLastSeenTimestamp())) {
+						validIP = false;
+						break;
+					}
+				}
+				if (!validIP)
+					break;
+			}
+
+			if (validIP)
+				vals.add(e.getIpv6Address());
+		}
+
+		return vals.toArray(new IPv6Address[vals.size()]);
+	}
+
+	@Override
+	public VlanVid[] getSwitchPortVlanIds(SwitchPort swp) {
+		TreeSet<VlanVid> vals = new TreeSet<VlanVid>();
+		for (Entity e : entities) {
+			if (e.switchDPID.equals(swp.getSwitchDPID())
+					&& e.switchPort.equals(swp.getPort())) {
+				if (e.getVlan() == null)
+					vals.add(VlanVid.ZERO);
+				else
+					vals.add(e.getVlan());
+			}
+		}
+		return vals.toArray(new VlanVid[vals.size()]);
+	}
+
+	@Override
+	public Date getLastSeen() {
+		Date d = null;
+		for (int i = 0; i < entities.length; i++) {
+			if (d == null
+					|| entities[i].getLastSeenTimestamp().compareTo(d) > 0)
+				d = entities[i].getLastSeenTimestamp();
+		}
+		return d;
+	}
+
+	// ***************
+	// Getters/Setters
+	// ***************
+
+	@Override
+	public IEntityClass getEntityClass() {
+		return entityClass;
+	}
+
+	public Entity[] getEntities() {
+		return entities;
+	}
+
+	public String getDHCPClientName() {
+		return dhcpClientName;
+	}
+
+	// ***************
+	// Utility Methods
+	// ***************
+
+	/**
+	 * Check whether the device contains the specified entity
+	 * 
+	 * @param entity
+	 *            the entity to search for
+	 * @return the index of the entity, or <0 if not found
+	 */
+	protected int entityIndex(Entity entity) {
+		return Arrays.binarySearch(entities, entity);
+	}
+
+	// ******
+	// Object
+	// ******
+
+	@Override
 	public int hashCode() {
 		final int prime = 31;
 		int result = 1;
@@ -742,7 +859,7 @@ public class Device implements IDevice {
 		return result;
 	}
 
-    @Override
+	@Override
 	public boolean equals(Object obj) {
 		if (this == obj)
 			return true;
@@ -761,26 +878,34 @@ public class Device implements IDevice {
 		return true;
 	}
 
-    @Override
-    public String toString() {
-        StringBuilder builder = new StringBuilder();
-        builder.append("Device [deviceKey=");
-        builder.append(deviceKey);
-        builder.append(", entityClass=");
-        builder.append(entityClass.getName());
-        builder.append(", MAC=");
-        builder.append(macAddressString);
-        builder.append(", IPs=[");
-        boolean isFirst = true;
-        for (IPv4Address ip: getIPv4Addresses()) {
-            if (!isFirst)
-                builder.append(", ");
-            isFirst = false;
-            builder.append(ip.toString());
-        }
-        builder.append("], APs=");
-        builder.append(Arrays.toString(getAttachmentPoints(true)));
-        builder.append("]");
-        return builder.toString();
-    }
+	@Override
+	public String toString() {
+		StringBuilder builder = new StringBuilder();
+		builder.append("Device [deviceKey=");
+		builder.append(deviceKey);
+		builder.append(", entityClass=");
+		builder.append(entityClass.getName());
+		builder.append(", MAC=");
+		builder.append(macAddressString);
+		builder.append(", IPv4s=[");
+		boolean isFirst = true;
+		for (IPv4Address ip : getIPv4Addresses()) {
+			if (!isFirst)
+				builder.append(", ");
+			isFirst = false;
+			builder.append(ip.toString());
+		}
+		builder.append("], IPv6s=[");
+		isFirst = true;
+		for (IPv6Address ip : getIPv6Addresses()) {
+			if (!isFirst)
+				builder.append(", ");
+			isFirst = false;
+			builder.append(ip.toString());
+		}
+		builder.append("], APs=");
+		builder.append(Arrays.toString(getAttachmentPoints(true)));
+		builder.append("]");
+		return builder.toString();
+	}
 }
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIndex.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIndex.java
index 0c5100498a87c1c94736a90e793d1968c282283e..dd3702cc9ebbef8fe603e39209ee3d8a6ea27a3b 100644
--- a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIndex.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIndex.java
@@ -114,4 +114,4 @@ public abstract class DeviceIndex {
         }
     }
 
-}
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIndexInterator.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIndexInterator.java
index 2015bbe60d648d86c33a582d2c9bae9b5274e5c8..6404ba662173b70dc7ac291bd8a5dc368a1dc320 100644
--- a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIndexInterator.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIndexInterator.java
@@ -55,5 +55,4 @@ public class DeviceIndexInterator implements Iterator<Device> {
     public void remove() {
         subIterator.remove();
     }
-
-}
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIterator.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIterator.java
index 4c7c4a4b15b676b3aeef0308ceb35a44e964f139..199271d14506f25b173648e2741a97f20a1f491b 100644
--- a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIterator.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIterator.java
@@ -23,6 +23,7 @@ import java.util.List;
 
 import org.projectfloodlight.openflow.types.DatapathId;
 import org.projectfloodlight.openflow.types.IPv4Address;
+import org.projectfloodlight.openflow.types.IPv6Address;
 import org.projectfloodlight.openflow.types.MacAddress;
 import org.projectfloodlight.openflow.types.OFPort;
 import org.projectfloodlight.openflow.types.VlanVid;
@@ -40,6 +41,7 @@ public class DeviceIterator extends FilterIterator<Device> {
     private MacAddress macAddress;
     private VlanVid vlan;
     private IPv4Address ipv4Address; 
+    private IPv6Address ipv6Address;
     private DatapathId switchDPID;
     private OFPort switchPort;
     
@@ -50,6 +52,7 @@ public class DeviceIterator extends FilterIterator<Device> {
      * @param macAddress The MAC address
      * @param vlan the VLAN
      * @param ipv4Address the ipv4 address
+     * @param ipv6Address the ipv6 address
      * @param switchDPID the switch DPID
      * @param switchPort the switch port
      */
@@ -58,6 +61,7 @@ public class DeviceIterator extends FilterIterator<Device> {
                           MacAddress macAddress,
                           VlanVid vlan, 
                           IPv4Address ipv4Address, 
+                          IPv6Address ipv6Address,
                           DatapathId switchDPID,
                           OFPort switchPort) {
         super(subIterator);
@@ -66,6 +70,7 @@ public class DeviceIterator extends FilterIterator<Device> {
         this.macAddress = macAddress;
         this.vlan = vlan;
         this.ipv4Address = ipv4Address;
+        this.ipv6Address = ipv6Address;
         this.switchDPID = switchDPID;
         this.switchPort = switchPort;
     }
@@ -86,35 +91,42 @@ public class DeviceIterator extends FilterIterator<Device> {
             }
             if (!match) return false;                
         }
-        if (macAddress != null) {
+        if (!macAddress.equals(MacAddress.NONE)) {
             if (!macAddress.equals(value.getMACAddress()))
                 return false;
         }
-        if (vlan != null) {
+        if (vlan != null) { /* VLAN is null, since VlanVid.ZERO is untagged */
             VlanVid[] vlans = value.getVlanId();
             List<VlanVid> searchableVlanList = Arrays.asList(vlans);
             if (!searchableVlanList.contains(vlan)) {
             	return false;
             }
         }
-        if (ipv4Address != null) {
+        if (!ipv4Address.equals(IPv4Address.NONE)) {
             IPv4Address[] ipv4Addresses = value.getIPv4Addresses();
             List<IPv4Address> searchableIPv4AddrList = Arrays.asList(ipv4Addresses);
             if (!searchableIPv4AddrList.contains(ipv4Address)) {
             	return false;
             }
         }
-        if (switchDPID != null || switchPort != null) {
+        if (!ipv6Address.equals(IPv6Address.NONE)) {
+            IPv6Address[] ipv6Addresses = value.getIPv6Addresses();
+            List<IPv6Address> searchableIPv6AddrList = Arrays.asList(ipv6Addresses);
+            if (!searchableIPv6AddrList.contains(ipv6Address)) {
+            	return false;
+            }
+        }
+        if (!switchDPID.equals(DatapathId.NONE) || !switchPort.equals(OFPort.ZERO)) {
             SwitchPort[] sps = value.getAttachmentPoints();
             if (sps == null) return false;
             
             match = false;
             for (SwitchPort sp : sps) {
-                if (switchDPID != null) {
+                if (!switchDPID.equals(DatapathId.NONE)) {
                     if (!switchDPID.equals(sp.getSwitchDPID()))
                         return false;
                 }
-                if (switchPort != null) {
+                if (!switchPort.equals(OFPort.ZERO)) {
                     if (!switchPort.equals(sp.getPort()))
                         return false;
                 }
@@ -125,4 +137,4 @@ public class DeviceIterator extends FilterIterator<Device> {
         }
         return true;
     }
-}
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java
index 7f9c023042ca47cb8780e635c66ad5aab94b32cd..cb5c0fcb25c22be4fbd76d03ac26a1a922cd3d5a 100755
--- a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java
@@ -39,6 +39,8 @@ import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
 
+import javax.annotation.Nonnull;
+
 import net.floodlightcontroller.core.FloodlightContext;
 import net.floodlightcontroller.core.HAListenerTypeMarker;
 import net.floodlightcontroller.core.IFloodlightProviderService;
@@ -76,6 +78,7 @@ import net.floodlightcontroller.packet.DHCP;
 import net.floodlightcontroller.packet.DHCPOption;
 import net.floodlightcontroller.packet.Ethernet;
 import net.floodlightcontroller.packet.IPv4;
+import net.floodlightcontroller.packet.IPv6;
 import net.floodlightcontroller.packet.UDP;
 import net.floodlightcontroller.packet.DHCP.DHCPOptionCode;
 import net.floodlightcontroller.restserver.IRestApiService;
@@ -92,6 +95,7 @@ import org.projectfloodlight.openflow.protocol.OFPacketIn;
 import org.projectfloodlight.openflow.protocol.OFVersion;
 import org.projectfloodlight.openflow.types.DatapathId;
 import org.projectfloodlight.openflow.types.IPv4Address;
+import org.projectfloodlight.openflow.types.IPv6Address;
 import org.projectfloodlight.openflow.types.MacAddress;
 import org.projectfloodlight.openflow.types.OFPort;
 import org.projectfloodlight.openflow.types.VlanVid;
@@ -240,7 +244,7 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT
 	 * The entity classifier currently in use
 	 */
 	protected IEntityClassifierService entityClassifier;
-
+	
 	/**
 	 * Used to cache state about specific entity classes
 	 */
@@ -360,7 +364,6 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT
 			if (oldDomain.getLong() < newDomain.getLong()) return -1;
 			else if (oldDomain.getLong() > newDomain.getLong()) return 1;
 
-
 			// Give preference to LOCAL always
 			if (oldPort != OFPort.LOCAL &&
 					newPort == OFPort.LOCAL) {
@@ -373,7 +376,7 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT
 			// We expect that the last seen of the new AP is higher than
 			// old AP, if it is not, just reverse and send the negative
 			// of the result.
-			if (oldAP.getActiveSince().after(newAP.getActiveSince()))
+			if (oldAP.getLastSeen().after(newAP.getLastSeen())) //TODO should this be lastSeen? @Ryan did change this from activeSince
 				return -compare(newAP, oldAP);
 
 			long activeOffset = 0;
@@ -440,16 +443,37 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT
 	}
 
 	@Override
-	public IDevice findDevice(MacAddress macAddress, VlanVid vlan,
-			IPv4Address ipv4Address, DatapathId switchDPID,
-			OFPort switchPort)
+	public IDevice findDevice(@Nonnull MacAddress macAddress, VlanVid vlan,
+			@Nonnull IPv4Address ipv4Address, @Nonnull IPv6Address ipv6Address,
+			@Nonnull DatapathId switchDPID, @Nonnull OFPort switchPort)
 					throws IllegalArgumentException {
-		if (vlan != null && vlan.getVlan() <= 0)
-			vlan = null;
-		if (ipv4Address != null && ipv4Address.getInt() == 0)
-			ipv4Address = null;
-		Entity e = new Entity(macAddress, vlan, ipv4Address, switchDPID,
-				switchPort, null);
+		if (macAddress == null) {
+    		throw new IllegalArgumentException("MAC address cannot be null. Try MacAddress.NONE if intention is 'no MAC'");
+    	}
+    	if (ipv4Address == null) {
+    		throw new IllegalArgumentException("IPv4 address cannot be null. Try IPv4Address.NONE if intention is 'no IPv4'");
+    	}
+    	if (ipv6Address == null) {
+    		throw new IllegalArgumentException("IPv6 address cannot be null. Try IPv6Address.NONE if intention is 'no IPv6'");
+    	}
+    	if (vlan == null) {
+    		throw new IllegalArgumentException("VLAN cannot be null. Try VlanVid.ZERO if intention is 'no VLAN / untagged'");
+    	}
+    	if (switchDPID == null) {
+    		throw new IllegalArgumentException("Switch DPID cannot be null. Try DatapathId.NONE if intention is 'no DPID'");
+    	}
+    	if (switchPort == null) {
+    		throw new IllegalArgumentException("Switch port cannot be null. Try OFPort.ZERO if intention is 'no port'");
+    	}
+		
+		Entity e = new Entity(macAddress, vlan, 
+				ipv4Address, ipv6Address, 
+				switchDPID, switchPort, Entity.NO_DATE);
+		
+		/*
+		 * allKeyFieldsPresent() will check if the entity key fields (e.g. MAC and VLAN)
+		 * have non-"zero" values i.e. are not set to e.g. MacAddress.NONE and VlanVid.ZERO
+		 */
 		if (!allKeyFieldsPresent(e, entityClassifier.getKeyFields())) {
 			throw new IllegalArgumentException("Not all key fields specified."
 					+ " Required fields: " + entityClassifier.getKeyFields());
@@ -458,17 +482,27 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT
 	}
 
 	@Override
-	public IDevice findClassDevice(IEntityClass entityClass, MacAddress macAddress,
-			VlanVid vlan, IPv4Address ipv4Address)
+	public IDevice findClassDevice(@Nonnull IEntityClass entityClass, @Nonnull MacAddress macAddress,
+			@Nonnull VlanVid vlan, @Nonnull IPv4Address ipv4Address, @Nonnull IPv6Address ipv6Address)
 					throws IllegalArgumentException {
-		if (vlan != null && vlan.getVlan() <= 0)
-			vlan = null;
-		if (ipv4Address != null && ipv4Address.getInt() == 0)
-			ipv4Address = null;
-		Entity e = new Entity(macAddress, vlan, ipv4Address,
-				null, null, null);
-		if (entityClass == null ||
-				!allKeyFieldsPresent(e, entityClass.getKeyFields())) {
+		if (entityClass == null) {
+    		throw new IllegalArgumentException("Entity class cannot be null.");
+    	}
+		if (macAddress == null) {
+    		throw new IllegalArgumentException("MAC address cannot be null. Try MacAddress.NONE if intention is 'no MAC'");
+    	}
+    	if (ipv4Address == null) {
+    		throw new IllegalArgumentException("IPv4 address cannot be null. Try IPv4Address.NONE if intention is 'no IPv4'");
+    	}
+    	if (ipv6Address == null) {
+    		throw new IllegalArgumentException("IPv6 address cannot be null. Try IPv6Address.NONE if intention is 'no IPv6'");
+    	}
+    	if (vlan == null) {
+    		throw new IllegalArgumentException("VLAN cannot be null. Try VlanVid.ZERO if intention is 'no VLAN / untagged'");
+    	}
+    	
+		Entity e = new Entity(macAddress, vlan, ipv4Address, ipv6Address, DatapathId.NONE, OFPort.ZERO, Entity.NO_DATE);
+		if (!allKeyFieldsPresent(e, entityClass.getKeyFields())) {
 			throw new IllegalArgumentException("Not all key fields and/or "
 					+ " no source device specified. Required fields: " +
 					entityClassifier.getKeyFields());
@@ -493,15 +527,33 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT
 	}
 
 	@Override
-	public Iterator<? extends IDevice> queryDevices(MacAddress macAddress,
+	public Iterator<? extends IDevice> queryDevices(@Nonnull MacAddress macAddress,
 			VlanVid vlan,
-			IPv4Address ipv4Address,
-			DatapathId switchDPID,
-			OFPort switchPort) {
+			@Nonnull IPv4Address ipv4Address,
+			@Nonnull IPv6Address ipv6Address,
+			@Nonnull DatapathId switchDPID,
+			@Nonnull OFPort switchPort) {
+		if (macAddress == null) {
+    		throw new IllegalArgumentException("MAC address cannot be null. Try MacAddress.NONE if intention is 'no MAC'");
+    	}
+    	if (ipv4Address == null) {
+    		throw new IllegalArgumentException("IPv4 address cannot be null. Try IPv4Address.NONE if intention is 'no IPv4'");
+    	}
+    	if (ipv6Address == null) {
+    		throw new IllegalArgumentException("IPv6 address cannot be null. Try IPv6Address.NONE if intention is 'no IPv6'");
+    	}
+    	/* VLAN can be null in this case, which means 'don't care' */
+    	if (switchDPID == null) {
+    		throw new IllegalArgumentException("Switch DPID cannot be null. Try DatapathId.NONE if intention is 'no DPID'");
+    	}
+    	if (switchPort == null) {
+    		throw new IllegalArgumentException("Switch port cannot be null. Try OFPort.ZERO if intention is 'no port'");
+    	}
+		
 		DeviceIndex index = null;
 		if (secondaryIndexMap.size() > 0) {
 			EnumSet<DeviceField> keys =
-					getEntityKeys(macAddress, vlan, ipv4Address,
+					getEntityKeys(macAddress, vlan, ipv4Address, ipv6Address,
 							switchDPID, switchPort);
 			index = secondaryIndexMap.get(keys);
 		}
@@ -515,9 +567,10 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT
 			Entity entity = new Entity(macAddress,
 					vlan,
 					ipv4Address,
+					ipv6Address,
 					switchDPID,
 					switchPort,
-					null);
+					Entity.NO_DATE);
 			deviceIterator =
 					new DeviceIndexInterator(this, index.queryByEntity(entity));
 		}
@@ -528,18 +581,37 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT
 						macAddress,
 						vlan,
 						ipv4Address,
+						ipv6Address,
 						switchDPID,
 						switchPort);
 		return di;
 	}
 
 	@Override
-	public Iterator<? extends IDevice> queryClassDevices(IEntityClass entityClass,
-			MacAddress macAddress,
-			VlanVid vlan,
-			IPv4Address ipv4Address,
-			DatapathId switchDPID,
-			OFPort switchPort) {
+	public Iterator<? extends IDevice> queryClassDevices(@Nonnull IEntityClass entityClass,
+			@Nonnull MacAddress macAddress,
+			@Nonnull VlanVid vlan,
+			@Nonnull IPv4Address ipv4Address,
+			@Nonnull IPv6Address ipv6Address,
+			@Nonnull DatapathId switchDPID,
+			@Nonnull OFPort switchPort) {
+		if (macAddress == null) {
+    		throw new IllegalArgumentException("MAC address cannot be null. Try MacAddress.NONE if intention is 'no MAC'");
+    	}
+    	if (ipv4Address == null) {
+    		throw new IllegalArgumentException("IPv4 address cannot be null. Try IPv4Address.NONE if intention is 'no IPv4'");
+    	}
+    	if (ipv6Address == null) {
+    		throw new IllegalArgumentException("IPv6 address cannot be null. Try IPv6Address.NONE if intention is 'no IPv6'");
+    	}
+    	/* VLAN can be null, which means 'don't care' */
+    	if (switchDPID == null) {
+    		throw new IllegalArgumentException("Switch DPID cannot be null. Try DatapathId.NONE if intention is 'no DPID'");
+    	}
+    	if (switchPort == null) {
+    		throw new IllegalArgumentException("Switch port cannot be null. Try OFPort.ZERO if intention is 'no port'");
+    	}
+    	
 		ArrayList<Iterator<Device>> iterators =
 				new ArrayList<Iterator<Device>>();
 		ClassState classState = getClassState(entityClass);
@@ -548,7 +620,7 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT
 		if (classState.secondaryIndexMap.size() > 0) {
 			EnumSet<DeviceField> keys =
 					getEntityKeys(macAddress, vlan, ipv4Address,
-							switchDPID, switchPort);
+							ipv6Address, switchDPID, switchPort);
 			index = classState.secondaryIndexMap.get(keys);
 		}
 
@@ -560,7 +632,7 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT
 				return new DeviceIterator(deviceMap.values().iterator(),
 						new IEntityClass[] { entityClass },
 						macAddress, vlan, ipv4Address,
-						switchDPID, switchPort);
+						ipv6Address, switchDPID, switchPort);
 			} else {
 				// scan the entire class
 				iter = new DeviceIndexInterator(this, index.getAll());
@@ -571,9 +643,10 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT
 					new Entity(macAddress,
 							vlan,
 							ipv4Address,
+							ipv6Address,
 							switchDPID,
 							switchPort,
-							null);
+							Entity.NO_DATE);
 			iter = new DeviceIndexInterator(this,
 					index.queryByEntity(entity));
 		}
@@ -582,16 +655,34 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT
 		return new MultiIterator<Device>(iterators.iterator());
 	}
 
-	protected Iterator<Device> getDeviceIteratorForQuery(MacAddress macAddress,
+	protected Iterator<Device> getDeviceIteratorForQuery(@Nonnull MacAddress macAddress,
 			VlanVid vlan,
-			IPv4Address ipv4Address,
-			DatapathId switchDPID,
-			OFPort switchPort) {
+			@Nonnull IPv4Address ipv4Address,
+			@Nonnull IPv6Address ipv6Address,
+			@Nonnull DatapathId switchDPID,
+			@Nonnull OFPort switchPort) {
+		if (macAddress == null) {
+    		throw new IllegalArgumentException("MAC address cannot be null. Try MacAddress.NONE if intention is 'no MAC'");
+    	}
+    	if (ipv4Address == null) {
+    		throw new IllegalArgumentException("IPv4 address cannot be null. Try IPv4Address.NONE if intention is 'no IPv4'");
+    	}
+    	if (ipv6Address == null) {
+    		throw new IllegalArgumentException("IPv6 address cannot be null. Try IPv6Address.NONE if intention is 'no IPv6'");
+    	}
+    	/* VLAN can be null, which means 'don't care' */
+    	if (switchDPID == null) {
+    		throw new IllegalArgumentException("Switch DPID cannot be null. Try DatapathId.NONE if intention is 'no DPID'");
+    	}
+    	if (switchPort == null) {
+    		throw new IllegalArgumentException("Switch port cannot be null. Try OFPort.ZERO if intention is 'no port'");
+    	}
+		
 		DeviceIndex index = null;
 		if (secondaryIndexMap.size() > 0) {
 			EnumSet<DeviceField> keys =
 					getEntityKeys(macAddress, vlan, ipv4Address,
-							switchDPID, switchPort);
+							ipv6Address, switchDPID, switchPort);
 			index = secondaryIndexMap.get(keys);
 		}
 
@@ -604,9 +695,10 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT
 			Entity entity = new Entity(macAddress,
 					vlan,
 					ipv4Address,
+					ipv6Address,
 					switchDPID,
 					switchPort,
-					null);
+					Entity.NO_DATE);
 			deviceIterator =
 					new DeviceIndexInterator(this, index.queryByEntity(entity));
 		}
@@ -617,6 +709,7 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT
 						macAddress,
 						vlan,
 						ipv4Address,
+						ipv6Address,
 						switchDPID,
 						switchPort);
 		return di;
@@ -694,6 +787,11 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT
 		public void deviceIPV4AddrChanged(IDevice device) {
 			generateDeviceEvent(device, "host-ipv4-addr-changed");
 		}
+		
+		@Override
+		public void deviceIPV6AddrChanged(IDevice device) {
+			generateDeviceEvent(device, "host-ipv6-addr-changed");
+		}
 
 		@Override
 		public void deviceVlanChanged(IDevice device) {
@@ -703,6 +801,8 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT
 		private void generateDeviceEvent(IDevice device, String reason) {
 			List<IPv4Address> ipv4Addresses =
 					new ArrayList<IPv4Address>(Arrays.asList(device.getIPv4Addresses()));
+			List<IPv6Address> ipv6Addresses =
+					new ArrayList<IPv6Address>(Arrays.asList(device.getIPv6Addresses()));
 			List<SwitchPort> oldAps =
 					new ArrayList<SwitchPort>(Arrays.asList(device.getOldAP()));
 			List<SwitchPort> currentAps =
@@ -712,6 +812,7 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT
 
 			debugEventCategory.newEventNoFlush(new DeviceEvent(device.getMACAddress(),
 					ipv4Addresses,
+					ipv6Addresses,
 					oldAps,
 					currentAps,
 					vlanIds, reason));
@@ -807,7 +908,8 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT
 	public void init(FloodlightModuleContext fmc) throws FloodlightModuleException {
 		this.perClassIndices =
 				new HashSet<EnumSet<DeviceField>>();
-		addIndex(true, EnumSet.of(DeviceField.IPV4));
+		addIndex(true, EnumSet.of(DeviceField.IPv4));
+		addIndex(true, EnumSet.of(DeviceField.IPv6));
 
 		this.deviceListeners = new ListenerDispatcher<String, IDeviceListener>();
 		this.suppressAPs = Collections.newSetFromMap(
@@ -1055,7 +1157,6 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT
 		@Override
 		public boolean isCallbackOrderingPostreq(HAListenerTypeMarker type,
 				String name) {
-			// TODO Auto-generated method stub
 			return false;
 		}
 
@@ -1173,22 +1274,37 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT
 	}
 
 	/**
-	 * Get sender IP address from packet if the packet is an ARP
+	 * Get sender IPv4 address from packet if the packet is an ARP
 	 * packet and if the source MAC address matches the ARP packets
 	 * sender MAC address.
 	 * @param eth
 	 * @param dlAddr
 	 * @return
 	 */
-	private IPv4Address getSrcNwAddr(Ethernet eth, MacAddress dlAddr) {
+	private IPv4Address getSrcIPv4AddrFromARP(Ethernet eth, MacAddress dlAddr) {
 		if (eth.getPayload() instanceof ARP) {
 			ARP arp = (ARP) eth.getPayload();
-			if ((arp.getProtocolType() == ARP.PROTO_TYPE_IP) && (MacAddress.of(arp.getSenderHardwareAddress()).equals(dlAddr))) {
-				return IPv4Address.of(arp.getSenderProtocolAddress());
+			if ((arp.getProtocolType() == ARP.PROTO_TYPE_IP) && (arp.getSenderHardwareAddress().equals(dlAddr))) {
+				return arp.getSenderProtocolAddress();
 			}
 		}
 		return IPv4Address.NONE;
 	}
+	
+	/**
+	 * Get sender IPv6 address from packet if the packet is ND
+	 * 
+	 * @param eth
+	 * @param dlAddr
+	 * @return
+	 */
+	private IPv6Address getSrcIPv6Addr(Ethernet eth) {
+		if (eth.getPayload() instanceof IPv6) {
+			IPv6 ipv6 = (IPv6) eth.getPayload();
+			return ipv6.getSourceAddress();
+		}
+		return IPv6Address.NONE;
+	}
 
 	/**
 	 * Parse an entity from an {@link Ethernet} packet.
@@ -1208,10 +1324,12 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT
 			return null;
 
 		VlanVid vlan = VlanVid.ofVlan(eth.getVlanID());
-		IPv4Address nwSrc = getSrcNwAddr(eth, dlAddr);
+		IPv4Address ipv4Src = getSrcIPv4AddrFromARP(eth, dlAddr);
+		IPv6Address ipv6Src = ipv4Src.equals(IPv4Address.NONE) ? getSrcIPv6Addr(eth) : IPv6Address.NONE;
 		return new Entity(dlAddr,
 				vlan,
-				nwSrc,
+				ipv4Src,
+				ipv6Src,
 				swdpid,
 				port,
 				new Date());
@@ -1231,7 +1349,7 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT
 
 		MacAddress dlAddr = eth.getSourceMACAddress();
 
-		MacAddress senderAddr = MacAddress.of(arp.getSenderHardwareAddress());
+		MacAddress senderAddr = arp.getSenderHardwareAddress();
 
 		if (dlAddr.equals(senderAddr)) return; // arp request
 
@@ -1239,15 +1357,16 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT
 		if (senderAddr.isBroadcast() || senderAddr.isMulticast())
 			return;
 		// Ignore zero sender mac
-		if (senderAddr.getLong() == 0)
+		if (senderAddr.equals(MacAddress.of(0)))
 			return;
 
 		VlanVid vlan = VlanVid.ofVlan(eth.getVlanID());
-		IPv4Address nwSrc = IPv4Address.of(arp.getSenderProtocolAddress());
+		IPv4Address nwSrc = arp.getSenderProtocolAddress();
 
 		Entity e =  new Entity(senderAddr,
-				((vlan.getVlan() >= 0) ? vlan : null),
-				((nwSrc.getInt() != 0) ? nwSrc : null),
+				vlan, /* will either be a valid tag or VlanVid.ZERO if untagged */
+				nwSrc,
+				IPv6Address.NONE, /* must be none for ARP */
 				swdpid,
 				port,
 				new Date());
@@ -1263,26 +1382,31 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT
 	protected Entity getDestEntityFromPacket(Ethernet eth) {
 		MacAddress dlAddr = eth.getDestinationMACAddress();
 		VlanVid vlan = VlanVid.ofVlan(eth.getVlanID());
-		IPv4Address nwDst = IPv4Address.NONE;
+		IPv4Address ipv4Dst = IPv4Address.NONE;
+		IPv6Address ipv6Dst = IPv6Address.NONE;
 
 		// Ignore broadcast/multicast destination
 		if (dlAddr.isBroadcast() || dlAddr.isMulticast())
 			return null;
 		// Ignore zero dest mac
-		if (dlAddr.getLong() == 0)
+		if (dlAddr.equals(MacAddress.of(0)))
 			return null;
 
 		if (eth.getPayload() instanceof IPv4) {
 			IPv4 ipv4 = (IPv4) eth.getPayload();
-			nwDst = ipv4.getDestinationAddress();
+			ipv4Dst = ipv4.getDestinationAddress();
+		} else if (eth.getPayload() instanceof IPv6) {
+			IPv6 ipv6 = (IPv6) eth.getPayload();
+			ipv6Dst = ipv6.getDestinationAddress();
 		}
 		
 		return new Entity(dlAddr,
 				vlan,
-				nwDst,
-				null,
-				null,
-				null);
+				ipv4Dst,
+				ipv6Dst,
+				DatapathId.NONE,
+				OFPort.ZERO,
+				Entity.NO_DATE);
 	}
 
 	/**
@@ -1497,12 +1621,13 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT
 				cntPacketOnInternalPortForKnownDevice.increment();
 				break;
 			}
+			
 			int entityindex = -1;
 			if ((entityindex = device.entityIndex(entity)) >= 0) {
 				// Entity already exists
 				// update timestamp on the found entity
 				Date lastSeen = entity.getLastSeenTimestamp();
-				if (lastSeen == null) {
+				if (lastSeen.equals(Entity.NO_DATE)) {
 					lastSeen = new Date();
 					entity.setLastSeenTimestamp(lastSeen);
 				}
@@ -1553,11 +1678,9 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT
 				boolean moved = device.updateAttachmentPoint(entity.getSwitchDPID(),
 						entity.getSwitchPort(),
 						entity.getLastSeenTimestamp());
-				// TODO: use update mechanism instead of sending the
-				// notification directly
 				if (moved) {
 					// we count device moved events in sendDeviceMovedNotification()
-					sendDeviceMovedNotification(device);
+					// TODO remove this. It's now done in the event handler as a result of the update above... sendDeviceMovedNotification(device);
 					if (logger.isTraceEnabled()) {
 						logger.trace("Device moved: attachment points {}," +
 								"entities {}", device.attachmentPoints,
@@ -1591,45 +1714,51 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT
 		return true;
 	}
 
-
-
-
-
 	protected EnumSet<DeviceField> findChangedFields(Device device,
 			Entity newEntity) {
 		EnumSet<DeviceField> changedFields =
-				EnumSet.of(DeviceField.IPV4,
+				EnumSet.of(DeviceField.IPv4,
+						DeviceField.IPv6,
 						DeviceField.VLAN,
 						DeviceField.SWITCH);
 
-		if (newEntity.getIpv4Address() == null)
-			changedFields.remove(DeviceField.IPV4);
-		if (newEntity.getVlan() == null)
+		/*
+		 * Do we really need this here?
+		 *
+		if (newEntity.getIpv4Address().equals(IPv4Address.NONE))
+			changedFields.remove(DeviceField.IPv4);
+		if (newEntity.getIpv6Address().equals(IPv6Address.NONE))
+			changedFields.remove(DeviceField.IPv6);
+		/*if (newEntity.getVlan().equals(VlanVid.ZERO)) TODO VLAN is ZERO here, since the actual Device and Entity must have some sort of VLAN, either untagged (ZERO) or some value 
 			changedFields.remove(DeviceField.VLAN);
-		if (newEntity.getSwitchDPID() == null ||
-				newEntity.getSwitchPort() == null)
-			changedFields.remove(DeviceField.SWITCH);
+		if (newEntity.getSwitchDPID().equals(DatapathId.NONE) ||
+				newEntity.getSwitchPort().equals(OFPort.ZERO))
+			changedFields.remove(DeviceField.SWITCH); 
 
-		if (changedFields.size() == 0) return changedFields;
+		if (changedFields.size() == 0) return changedFields; */
 
 		for (Entity entity : device.getEntities()) {
-			if (newEntity.getIpv4Address() == null ||
-					(entity.getIpv4Address() != null &&
-					entity.getIpv4Address().equals(newEntity.getIpv4Address())))
-				changedFields.remove(DeviceField.IPV4);
-			if (newEntity.getVlan() == null ||
-					(entity.getVlan() != null &&
-					entity.getVlan().equals(newEntity.getVlan())))
+			if (newEntity.getIpv4Address().equals(IPv4Address.NONE) || /* NONE means 'not in this packet' */
+					entity.getIpv4Address().equals(newEntity.getIpv4Address())) /* these (below) might be defined and if they are and changed, then the device has changed */
+				changedFields.remove(DeviceField.IPv4);
+			if (newEntity.getIpv6Address().equals(IPv6Address.NONE) || /* NONE means 'not in this packet' */
+					entity.getIpv6Address().equals(newEntity.getIpv6Address()))
+				changedFields.remove(DeviceField.IPv6);
+			if (entity.getVlan().equals(newEntity.getVlan())) /* these (below) must be defined in each and every packet-in, and if different signal a device field change */
 				changedFields.remove(DeviceField.VLAN);
-			if (newEntity.getSwitchDPID() == null ||
-					newEntity.getSwitchPort() == null ||
-					(entity.getSwitchDPID() != null &&
-					entity.getSwitchPort() != null &&
-					entity.getSwitchDPID().equals(newEntity.getSwitchDPID()) &&
+			if (newEntity.getSwitchDPID().equals(DatapathId.NONE) ||
+					newEntity.getSwitchPort().equals(OFPort.ZERO) ||
+					(entity.getSwitchDPID().equals(newEntity.getSwitchDPID()) &&
 					entity.getSwitchPort().equals(newEntity.getSwitchPort())))
 				changedFields.remove(DeviceField.SWITCH);
 		}
 
+		if (changedFields.contains(DeviceField.SWITCH)) {
+			if (!isValidAttachmentPoint(newEntity.getSwitchDPID(), newEntity.getSwitchPort())) {
+				changedFields.remove(DeviceField.SWITCH);
+			}
+		}
+		
 		return changedFields;
 	}
 
@@ -1669,12 +1798,15 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT
 			 case CHANGE:
 				 for (DeviceField field : update.fieldsChanged) {
 					 switch (field) {
-					 case IPV4:
+					 case IPv4:
 						 listener.deviceIPV4AddrChanged(update.device);
 						 break;
+					 case IPv6:
+						 listener.deviceIPV6AddrChanged(update.device);
+						 break;
 					 case SWITCH:
 					 case PORT:
-						 //listener.deviceMoved(update.device);
+						 listener.deviceMoved(update.device); // TODO why was this commented out?
 						 break;
 					 case VLAN:
 						 listener.deviceVlanChanged(update.device);
@@ -1702,19 +1834,26 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT
 			 case MAC:
 				 // MAC address is always present
 				 break;
-			 case IPV4:
-				 if (e.ipv4Address == null) return false;
+			 case IPv4:
+			 case IPv6:
+				 if (e.ipv4Address.equals(IPv4Address.NONE) && e.ipv6Address.equals(IPv6Address.NONE)) {
+					 return false; // mutually exclusive
+				 }
 				 break;
 			 case SWITCH:
-				 if (e.switchDPID == null) return false;
+				 if (e.switchDPID.equals(DatapathId.NONE)) {
+					 return false;
+				 }
 				 break;
 			 case PORT:
-				 if (e.switchPort == null) return false;
+				 if (e.switchPort.equals(OFPort.ZERO)) {
+					 return false;
+				 }
 				 break;
 			 case VLAN:
-				 // FIXME: vlan==null is ambiguous: it can mean: not present
-				 // or untagged
-				 //if (e.vlan == null) return false;
+				 if (e.vlan == null) { /* VLAN is null for 'don't care' or 'unspecified'. It's VlanVid.ZERO for untagged. */
+					 return false; 	   /* For key field of VLAN, the VLAN **MUST** be set to either ZERO or some value. */
+				 }
 				 break;
 			 default:
 				 // we should never get here. unless somebody extended
@@ -1819,7 +1958,7 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT
 				 toRemove.clear();
 				 toKeep.clear();
 				 for (Entity e : d.getEntities()) {
-					 if (e.getLastSeenTimestamp() != null &&
+					 if (!e.getLastSeenTimestamp().equals(Entity.NO_DATE) &&
 							 0 > e.getLastSeenTimestamp().compareTo(cutoff)) {
 						 // individual entity needs to be removed
 						 toRemove.add(e);
@@ -1937,20 +2076,19 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT
 		 }
 	 }
 
-	 private EnumSet<DeviceField> getEntityKeys(MacAddress macAddress,
-			 VlanVid vlan,
-			 IPv4Address ipv4Address,
-			 DatapathId switchDPID,
-			 OFPort switchPort) {
-		 // FIXME: vlan==null is a valid search. Need to handle this
-		 // case correctly. Note that the code will still work correctly.
-		 // But we might do a full device search instead of using an index.
+	 private EnumSet<DeviceField> getEntityKeys(@Nonnull MacAddress macAddress,
+			 VlanVid vlan, /* A null VLAN means 'don't care'; VlanVid.ZERO means 'untagged' */
+			 @Nonnull IPv4Address ipv4Address,
+			 @Nonnull IPv6Address ipv6Address,
+			 @Nonnull DatapathId switchDPID,
+			 @Nonnull OFPort switchPort) {
 		 EnumSet<DeviceField> keys = EnumSet.noneOf(DeviceField.class);
-		 if (macAddress != null) keys.add(DeviceField.MAC);
-		 if (vlan != null) keys.add(DeviceField.VLAN);
-		 if (ipv4Address != null) keys.add(DeviceField.IPV4);
-		 if (switchDPID != null) keys.add(DeviceField.SWITCH);
-		 if (switchPort != null) keys.add(DeviceField.PORT);
+		 if (!macAddress.equals(MacAddress.NONE)) keys.add(DeviceField.MAC);
+		 if (vlan != null) keys.add(DeviceField.VLAN); /* TODO verify fix. null means 'don't care' and will conduct full search; VlanVid.ZERO means 'untagged' and only uses untagged index */
+		 if (!ipv4Address.equals(IPv4Address.NONE)) keys.add(DeviceField.IPv4);
+		 if (!ipv6Address.equals(IPv6Address.NONE)) keys.add(DeviceField.IPv6);
+		 if (!switchDPID.equals(DatapathId.NONE)) keys.add(DeviceField.SWITCH);
+		 if (!switchPort.equals(OFPort.ZERO)) keys.add(DeviceField.PORT);
 		 return keys;
 	 }
 
@@ -1969,7 +2107,7 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT
 		 return new Device(this, deviceKey, entity, entityClass);
 	 }
 
-	 // TODO: FIX THIS.
+	 // TODO: FIX THIS. What's 'this' that needs fixing?
 	 protected Device allocateDevice(Long deviceKey,
 			 String dhcpClientName,
 			 List<AttachmentPoint> aps,
@@ -1986,7 +2124,8 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT
 		 return new Device(device, entity, insertionpoint);
 	 }
 
-	 //not used
+	 //not used 
+	 /* TODO then let's get rid of it?
 	 protected Device allocateDevice(Device device, Set <Entity> entities) {
 		 List <AttachmentPoint> newPossibleAPs =
 				 new ArrayList<AttachmentPoint>();
@@ -2014,7 +2153,7 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT
 				 entities, device.getEntityClass());
 		 d.updateAttachmentPoint();
 		 return d;
-	 }
+	 } */
 
 	 // *********************
 	 // ITopologyListener
@@ -2033,7 +2172,6 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT
 				 }
 			 }
 		 }
-
 		 while (diter.hasNext()) {
 			 Device d = diter.next();
 			 if (d.updateAttachmentPoint()) {
@@ -2353,6 +2491,7 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT
 							 // Do we have a device for this entity??
 									 IDevice d = findDevice(MacAddress.of(se.macAddress), VlanVid.ofVlan(se.vlan),
 											 IPv4Address.of(se.ipv4Address),
+											 IPv6Address.NONE,
 											 DatapathId.of(se.switchDPID),
 											 OFPort.of(se.switchPort));
 									 if (d != null) {
@@ -2408,8 +2547,10 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT
 	 private class DeviceEvent {
 		 @EventColumn(name = "MAC", description = EventFieldType.MAC)
 		 private final MacAddress macAddress;
-		 @EventColumn(name = "IPs", description = EventFieldType.IPv4)
+		 @EventColumn(name = "IPv4s", description = EventFieldType.IPv4)
 		 private final List<IPv4Address> ipv4Addresses;
+		 @EventColumn(name = "IPv6s", description = EventFieldType.IPv6)
+		 private final List<IPv6Address> ipv6Addresses;
 		 @EventColumn(name = "Old Attachment Points",
 				 description = EventFieldType.COLLECTION_ATTACHMENT_POINT)
 		 private final List<SwitchPort> oldAttachmentPoints;
@@ -2422,16 +2563,18 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT
 		 private final String reason;
 
 		 public DeviceEvent(MacAddress macAddress, List<IPv4Address> ipv4Addresses,
+				 List<IPv6Address> ipv6Addresses,
 				 List<SwitchPort> oldAttachmentPoints,
 				 List<SwitchPort> currentAttachmentPoints,
 				 List<VlanVid> vlanIds, String reason) {
 			 super();
 			 this.macAddress = macAddress;
 			 this.ipv4Addresses = ipv4Addresses;
+			 this.ipv6Addresses = ipv6Addresses;
 			 this.oldAttachmentPoints = oldAttachmentPoints;
 			 this.currentAttachmentPoints = currentAttachmentPoints;
 			 this.vlanIds = vlanIds;
 			 this.reason = reason;
 		 }
 	 }
-}
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceMultiIndex.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceMultiIndex.java
index df47d32b2558df200cf8f02154cbde6444411ff4..a603696054c8339b906fed8256411dc818c5d5aa 100644
--- a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceMultiIndex.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceMultiIndex.java
@@ -78,7 +78,7 @@ public class DeviceMultiIndex extends DeviceIndex {
         Collection<Long> devices = null;
 
         IndexedEntity ie = new IndexedEntity(keyFields, entity);
-        if (!ie.hasNonNullKeys()) return false;
+        if (!ie.hasNonZeroOrNonNullKeys()) return false;
 
         devices = index.get(ie);
         if (devices == null) {
@@ -106,4 +106,4 @@ public class DeviceMultiIndex extends DeviceIndex {
         if (devices != null)
             devices.remove(deviceKey);
     }
-}
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceSyncRepresentation.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceSyncRepresentation.java
index 82fb832e5d7049fc0342b7f7a10caabcb0891567..d0846c2a25abf2be1cb3323888fe64714e0c4f3d 100644
--- a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceSyncRepresentation.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceSyncRepresentation.java
@@ -9,6 +9,7 @@ import java.util.List;
 
 import org.projectfloodlight.openflow.types.DatapathId;
 import org.projectfloodlight.openflow.types.IPv4Address;
+import org.projectfloodlight.openflow.types.IPv6Address;
 import org.projectfloodlight.openflow.types.MacAddress;
 import org.projectfloodlight.openflow.types.OFPort;
 import org.projectfloodlight.openflow.types.VlanVid;
@@ -19,152 +20,160 @@ import net.floodlightcontroller.devicemanager.SwitchPort;
 import com.fasterxml.jackson.annotation.JsonProperty;
 
 public class DeviceSyncRepresentation {
-    public static class SyncEntity implements Comparable<SyncEntity> {
-        @JsonProperty
-        public long macAddress;
-        @JsonProperty
-        public int ipv4Address;
-        @JsonProperty
-        public short vlan;
-        @JsonProperty
-        public long switchDPID;
-        @JsonProperty
-        public int switchPort;
-        @JsonProperty
-        public Date lastSeenTimestamp;
-        @JsonProperty
-        public Date activeSince;
-
-        public SyncEntity() {
-            // do nothing;
-        }
-
-        public SyncEntity(Entity e) {
-            this.macAddress = (e.getMacAddress() != null ? e.getMacAddress().getLong() : 0);
-            this.ipv4Address = (e.getIpv4Address() != null ? e.getIpv4Address().getInt() : 0);
-            this.vlan = (e.getVlan() != null ? e.getVlan().getVlan() : -1);
-            this.switchDPID = (e.getSwitchDPID() != null ? e.getSwitchDPID().getLong() : 0);
-            this.switchPort = (e.getSwitchPort() != null ? e.getSwitchPort().getPortNumber() : 0);
-            if (e.getLastSeenTimestamp() == null)
-                this.lastSeenTimestamp = null;
-            else
-                this.lastSeenTimestamp = new Date(e.getLastSeenTimestamp().getTime());
-            if (e.getActiveSince() == null)
-                this.activeSince = null;
-            else
-                this.activeSince = new Date(e.getActiveSince().getTime());
-        }
-
-        public Entity asEntity() {
-            Entity e = new Entity(macAddress == 0 ? null : MacAddress.of(macAddress), 
-            		vlan == -1 ? null : VlanVid.ofVlan(vlan), 
-            		ipv4Address == 0 ? null : IPv4Address.of(ipv4Address), 
-            		switchDPID == 0 ? null : DatapathId.of(switchDPID),
-                    switchPort == 0 ? null : OFPort.of(switchPort), 
-                    lastSeenTimestamp);
-            e.setActiveSince(activeSince);
-            return e;
-        }
-
-        @Override
-        public int compareTo(SyncEntity other) {
-            return lastSeenTimestamp.compareTo(other.lastSeenTimestamp);
-        }
-
-        @Override
-        public String toString() {
-            return asEntity().toString();
-        }
-    }
-
-    private String key;
-    private List<SyncEntity> entities;
-
-    public DeviceSyncRepresentation() {
-        // do nothing
-    }
-
-    public DeviceSyncRepresentation(Device device) {
-        this.key = computeKey(device);
-        this.entities = new ArrayList<SyncEntity>();
-        // FIXME: do we need the APs with errors as well??
-        // FIXME
-        SwitchPort[] aps = device.getAttachmentPoints();
-        for(Entity e: device.getEntities()) {
-            // Add the entities from the device only if they either don't
-            // have a switch/port or if they are an attachment point or
-            // if they have an IP address.
-            if (!e.hasSwitchPort()) {
-                this.entities.add(new SyncEntity(e));
-            } else if (isAttachmentPointEntity(aps, e)) {
-                this.entities.add(new SyncEntity(e));
-            } else if (e.getIpv4Address() != null) {
-                this.entities.add(new SyncEntity(e));
-            }
-        }
-        Collections.sort(this.entities);
-    }
-
-    private static boolean isAttachmentPointEntity(SwitchPort[] aps, Entity e) {
-        if (!e.hasSwitchPort())
-            return false;
-        for (SwitchPort p: aps) {
-            if (e.getSwitchDPID().equals(p.getSwitchDPID()) &&
-                    e.getSwitchPort().equals(p.getPort())) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    static String computeKey(Device d) {
-        StringBuilder bld = new StringBuilder(d.getEntityClass().getName());
-        bld.append("::");
-        EnumSet<DeviceField> keyFields = d.getEntityClass().getKeyFields();
-        if (keyFields.contains(DeviceField.MAC)) {
-            bld.append(d.getMACAddressString());
-            bld.append("::");
-        }
-        if (keyFields.contains(DeviceField.VLAN)) {
-            if (d.getVlanId() != null)
-                bld.append(Arrays.toString(d.getVlanId()));
-            bld.append("::");
-        }
-        if (keyFields.contains(DeviceField.SWITCH) ||
-                keyFields.contains(DeviceField.PORT) ) {
-            if (d.getAttachmentPoints(true) != null)
-                bld.append(Arrays.toString(d.getAttachmentPoints(true)));
-            bld.append("::");
-        }
-        if (keyFields.contains(DeviceField.IPV4)) {
-            if (d.getIPv4Addresses() != null)
-                bld.append(Arrays.toString(d.getIPv4Addresses()));
-            bld.append("::");
-        }
-        return bld.toString();
-    }
-
-    public String getKey() {
-        return key;
-    }
-    public void setKey(String key) {
-        this.key = key;
-    }
-    public List<SyncEntity> getEntities() {
-        return entities;
-    }
-    public void setEntities(List<SyncEntity> entities) {
-        if (entities == null) {
-            this.entities = null;
-        } else {
-            List<SyncEntity> tmp = new ArrayList<SyncEntity>(entities);
-            Collections.sort(tmp);
-            this.entities = tmp;
-        }
-    }
-
-    @Override
-    public String toString() {
-        return key;
-    }
+	public static class SyncEntity implements Comparable<SyncEntity> {
+		@JsonProperty
+		public long macAddress;
+		@JsonProperty
+		public int ipv4Address; // TODO Sync IPv6 address
+		@JsonProperty
+		public short vlan;
+		@JsonProperty
+		public long switchDPID;
+		@JsonProperty
+		public int switchPort;
+		@JsonProperty
+		public Date lastSeenTimestamp;
+		@JsonProperty
+		public Date activeSince;
+
+		public SyncEntity() {
+			// do nothing;
+		}
+
+		public SyncEntity(Entity e) {
+			this.macAddress = (e.getMacAddress() != null ? e.getMacAddress().getLong() : 0);
+			this.ipv4Address = (e.getIpv4Address() != null ? e.getIpv4Address().getInt() : 0);
+			this.vlan = (e.getVlan() != null ? e.getVlan().getVlan() : -1);
+			this.switchDPID = (e.getSwitchDPID() != null ? e.getSwitchDPID().getLong() : 0);
+			this.switchPort = (e.getSwitchPort() != null ? e.getSwitchPort().getPortNumber() : 0);
+			if (e.getLastSeenTimestamp() == null)
+				this.lastSeenTimestamp = null;
+			else
+				this.lastSeenTimestamp = new Date(e.getLastSeenTimestamp().getTime());
+			if (e.getActiveSince() == null)
+				this.activeSince = null;
+			else
+				this.activeSince = new Date(e.getActiveSince().getTime());
+		}
+
+		public Entity asEntity() {
+			Entity e = new Entity(MacAddress.of(macAddress), 
+					VlanVid.ofVlan(vlan), 
+					IPv4Address.of(ipv4Address), 
+					IPv6Address.NONE,
+					DatapathId.of(switchDPID),
+					OFPort.of(switchPort), 
+					lastSeenTimestamp);
+			e.setActiveSince(activeSince);
+			return e;
+		}
+
+		@Override
+		public int compareTo(SyncEntity other) {
+			return lastSeenTimestamp.compareTo(other.lastSeenTimestamp);
+		}
+
+		@Override
+		public String toString() {
+			return asEntity().toString();
+		}
+	}
+
+	private String key;
+	private List<SyncEntity> entities;
+
+	public DeviceSyncRepresentation() {
+		// do nothing
+	}
+
+	public DeviceSyncRepresentation(Device device) {
+		this.key = computeKey(device);
+		this.entities = new ArrayList<SyncEntity>();
+		// FIXME: do we need the APs with errors as well??
+		// FIXME
+		SwitchPort[] aps = device.getAttachmentPoints();
+		for (Entity e : device.getEntities()) {
+			// Add the entities from the device only if they either don't
+			// have a switch/port or if they are an attachment point or
+			// if they have an IP address.
+			if (!e.hasSwitchPort()) {
+				this.entities.add(new SyncEntity(e));
+			} else if (isAttachmentPointEntity(aps, e)) {
+				this.entities.add(new SyncEntity(e));
+			} else if (!e.getIpv4Address().equals(IPv4Address.NONE)) {
+				this.entities.add(new SyncEntity(e));
+			}
+		}
+		Collections.sort(this.entities);
+	}
+
+	private static boolean isAttachmentPointEntity(SwitchPort[] aps, Entity e) {
+		if (!e.hasSwitchPort())
+			return false;
+		for (SwitchPort p : aps) {
+			if (e.getSwitchDPID().equals(p.getSwitchDPID()) &&
+					e.getSwitchPort().equals(p.getPort())
+					&& (!e.getSwitchDPID().equals(DatapathId.NONE) ||
+					!e.getSwitchPort().equals(OFPort.ZERO))) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	static String computeKey(Device d) {
+		StringBuilder bld = new StringBuilder(d.getEntityClass().getName());
+		bld.append("::");
+		EnumSet<DeviceField> keyFields = d.getEntityClass().getKeyFields();
+		if (keyFields.contains(DeviceField.MAC)) {
+			bld.append(d.getMACAddressString());
+			bld.append("::");
+		}
+		if (keyFields.contains(DeviceField.VLAN)) {
+			if (d.getVlanId() != null)
+				bld.append(Arrays.toString(d.getVlanId()));
+			bld.append("::");
+		}
+		if (keyFields.contains(DeviceField.SWITCH) ||
+				keyFields.contains(DeviceField.PORT) ) {
+			if (d.getAttachmentPoints(true) != null)
+				bld.append(Arrays.toString(d.getAttachmentPoints(true)));
+			bld.append("::");
+		}
+		if (keyFields.contains(DeviceField.IPv4)) {
+			if (d.getIPv4Addresses() != null)
+				bld.append(Arrays.toString(d.getIPv4Addresses()));
+			bld.append("::");
+		}
+		if (keyFields.contains(DeviceField.IPv6)) {
+			if (d.getIPv6Addresses() != null)
+				bld.append(Arrays.toString(d.getIPv6Addresses()));
+			bld.append("::");
+		}
+		return bld.toString();
+	}
+
+	public String getKey() {
+		return key;
+	}
+	public void setKey(String key) {
+		this.key = key;
+	}
+	public List<SyncEntity> getEntities() {
+		return entities;
+	}
+	public void setEntities(List<SyncEntity> entities) {
+		if (entities == null) {
+			this.entities = null;
+		} else {
+			List<SyncEntity> tmp = new ArrayList<SyncEntity>(entities);
+			Collections.sort(tmp);
+			this.entities = tmp;
+		}
+	}
+
+	@Override
+	public String toString() {
+		return key;
+	}
 }
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceUniqueIndex.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceUniqueIndex.java
index e373018bff2bc02a02a41384ae58daadaf45cb0b..3917f493b0788f409babae395de503cba71f0e6a 100644
--- a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceUniqueIndex.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceUniqueIndex.java
@@ -64,7 +64,7 @@ public class DeviceUniqueIndex extends DeviceIndex {
     public boolean updateIndex(Device device, Long deviceKey) {
         for (Entity e : device.entities) {
             IndexedEntity ie = new IndexedEntity(keyFields, e);
-            if (!ie.hasNonNullKeys()) continue;
+            if (!ie.hasNonZeroOrNonNullKeys()) continue;
 
             Long ret = index.putIfAbsent(ie, deviceKey);
             if (ret != null && !ret.equals(deviceKey)) {
@@ -80,7 +80,7 @@ public class DeviceUniqueIndex extends DeviceIndex {
     @Override
     public boolean updateIndex(Entity entity, Long deviceKey) {
         IndexedEntity ie = new IndexedEntity(keyFields, entity);
-        if (!ie.hasNonNullKeys()) return false;
+        if (!ie.hasNonZeroOrNonNullKeys()) return false;
         index.put(ie, deviceKey);
         return true;
     }
@@ -113,5 +113,4 @@ public class DeviceUniqueIndex extends DeviceIndex {
             return null;
         return deviceKey;
     }
-
-}
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/Entity.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/Entity.java
index 79c0199feae753336072b497e609ae891e13f81b..d1d24be6f19cfd8386a414827ab5bf878b6e1c2b 100644
--- a/src/main/java/net/floodlightcontroller/devicemanager/internal/Entity.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/Entity.java
@@ -19,7 +19,10 @@ package net.floodlightcontroller.devicemanager.internal;
 
 import java.util.Date;
 
+import javax.annotation.Nonnull;
+
 import net.floodlightcontroller.core.web.serializers.IPv4Serializer;
+import net.floodlightcontroller.core.web.serializers.IPv6Serializer;
 import net.floodlightcontroller.core.web.serializers.DPIDSerializer;
 import net.floodlightcontroller.core.web.serializers.OFPortSerializer;
 import net.floodlightcontroller.core.web.serializers.VlanVidSerializer;
@@ -30,6 +33,7 @@ import com.fasterxml.jackson.databind.annotation.JsonSerialize;
 
 import org.projectfloodlight.openflow.types.DatapathId;
 import org.projectfloodlight.openflow.types.IPv4Address;
+import org.projectfloodlight.openflow.types.IPv6Address;
 import org.projectfloodlight.openflow.types.MacAddress;
 import org.projectfloodlight.openflow.types.OFPort;
 import org.projectfloodlight.openflow.types.VlanVid;
@@ -55,17 +59,25 @@ public class Entity implements Comparable<Entity> {
      */
     protected static int ACTIVITY_TIMEOUT = 30000;
     
+    protected static final Date NO_DATE = new Date(0); /* Jan 1st 1970 00:00:00 */
+    
     /**
      * The MAC address associated with this entity
      */
     protected MacAddress macAddress;
     
     /**
-     * The IP address associated with this entity, or null if no IP learned
+     * The IPv4 address associated with this entity, or null if no IP learned
      * from the network observation associated with this entity
      */
     protected IPv4Address ipv4Address;
     
+    /**
+     * The IPv6 address associated with this entity, or null if no IP learned
+     * from the network observation associated with this entity
+     */
+    protected IPv6Address ipv6Address;
+    
     /**
      * The VLAN tag on this entity, or null if untagged
      */
@@ -108,15 +120,37 @@ public class Entity implements Comparable<Entity> {
      * @param macAddress
      * @param vlan
      * @param ipv4Address
+     * @param ipv6Address
      * @param switchDPID
      * @param switchPort
      * @param lastSeenTimestamp
      */
-    public Entity(MacAddress macAddress, VlanVid vlan, 
-                  IPv4Address ipv4Address, DatapathId switchDPID, OFPort switchPort, 
-                  Date lastSeenTimestamp) {
+    public Entity(@Nonnull MacAddress macAddress, VlanVid vlan, @Nonnull IPv4Address ipv4Address, 
+    		@Nonnull IPv6Address ipv6Address, @Nonnull DatapathId switchDPID, @Nonnull OFPort switchPort, 
+                  @Nonnull Date lastSeenTimestamp) {
+    	if (macAddress == null) {
+    		throw new IllegalArgumentException("MAC address cannot be null. Try MacAddress.NONE if intention is 'no MAC'");
+    	}
+    	if (ipv4Address == null) {
+    		throw new IllegalArgumentException("IPv4 address cannot be null. Try IPv4Address.NONE if intention is 'no IPv4'");
+    	}
+    	if (ipv6Address == null) {
+    		throw new IllegalArgumentException("IPv6 address cannot be null. Try IPv6Address.NONE if intention is 'no IPv6'");
+    	}
+    	/* VLAN can be null for 'don't care' in query searches */
+    	if (switchDPID == null) {
+    		throw new IllegalArgumentException("Switch DPID cannot be null. Try DatapathId.NONE if intention is 'no DPID'");
+    	}
+    	if (switchPort == null) {
+    		throw new IllegalArgumentException("Switch port cannot be null. Try OFPort.ZERO if intention is 'no port'");
+    	}
+    	if (lastSeenTimestamp == null) {
+    		throw new IllegalArgumentException("Last seen time stamp cannot be null. Try Entity.NO_DATE if intention is 'no time'");
+    	}
+    	
         this.macAddress = macAddress;
         this.ipv4Address = ipv4Address;
+        this.ipv6Address = ipv6Address;
         this.vlan = vlan;
         this.switchDPID = switchDPID;
         this.switchPort = switchPort;
@@ -138,6 +172,11 @@ public class Entity implements Comparable<Entity> {
         return ipv4Address;
     }
 
+    @JsonSerialize(using=IPv6Serializer.class)
+    public IPv6Address getIpv6Address() {
+        return ipv6Address;
+    }
+    
     @JsonSerialize(using=VlanVidSerializer.class)
     public VlanVid getVlan() {
         return vlan;
@@ -169,7 +208,8 @@ public class Entity implements Comparable<Entity> {
      * @see {@link Entity#activeSince}
      */
     public void setLastSeenTimestamp(Date lastSeenTimestamp) {
-        if (activeSince == null || (activeSince.getTime() + ACTIVITY_TIMEOUT) < lastSeenTimestamp.getTime())
+        if (activeSince.equals(Entity.NO_DATE) ||
+        		(activeSince.getTime() + ACTIVITY_TIMEOUT) < lastSeenTimestamp.getTime())
             this.activeSince = lastSeenTimestamp;
         this.lastSeenTimestamp = lastSeenTimestamp;
     }
@@ -188,6 +228,8 @@ public class Entity implements Comparable<Entity> {
 		int result = 1;
 		result = prime * result
 				+ ((ipv4Address == null) ? 0 : ipv4Address.hashCode());
+		result = prime * result
+				+ ((ipv6Address == null) ? 0 : ipv6Address.hashCode());
 		result = prime * result
 				+ ((macAddress == null) ? 0 : macAddress.hashCode());
 		result = prime * result
@@ -207,11 +249,18 @@ public class Entity implements Comparable<Entity> {
 		if (getClass() != obj.getClass())
 			return false;
 		Entity other = (Entity) obj;
+		if (hashCode() != obj.hashCode())
+			return false;
 		if (ipv4Address == null) {
 			if (other.ipv4Address != null)
 				return false;
 		} else if (!ipv4Address.equals(other.ipv4Address))
 			return false;
+		if (ipv6Address == null) {
+			if (other.ipv6Address != null)
+				return false;
+		} else if (!ipv6Address.equals(other.ipv6Address))
+			return false;
 		if (macAddress == null) {
 			if (other.macAddress != null)
 				return false;
@@ -235,8 +284,6 @@ public class Entity implements Comparable<Entity> {
 		return true;
 	}
 
-    
-    
     @Override
     public String toString() {
         StringBuilder builder = new StringBuilder();
@@ -252,6 +299,12 @@ public class Entity implements Comparable<Entity> {
         } else {
             builder.append("null");
         }
+        builder.append(", ipv6Address=");
+        if (ipv4Address != null) {
+            builder.append(ipv6Address.toString());
+        } else {
+            builder.append("null");
+        }
         builder.append(", vlan=");
         if (vlan != null) {
             builder.append(vlan.getVlan());
@@ -272,13 +325,14 @@ public class Entity implements Comparable<Entity> {
         }
         builder.append(", lastSeenTimestamp=");
         if (lastSeenTimestamp != null) {
-            builder.append(lastSeenTimestamp == null? "null" : lastSeenTimestamp.getTime());
+            builder.append(lastSeenTimestamp == null? "null" : lastSeenTimestamp.toString());
+            
         } else {
             builder.append("null");
         }
         builder.append(", activeSince=");
         if (activeSince != null) {
-            builder.append(activeSince == null? "null" : activeSince.getTime());
+            builder.append(activeSince == null? "null" : activeSince.toString());
         } else {
             builder.append("null");
         }
@@ -315,6 +369,14 @@ public class Entity implements Comparable<Entity> {
         else
             r = ipv4Address.compareTo(o.ipv4Address);
         if (r != 0) return r;
+        
+        if (ipv6Address == null)
+            r = o.ipv6Address == null ? 0 : -1;
+        else if (o.ipv6Address == null)
+            r = 1;
+        else
+            r = ipv6Address.compareTo(o.ipv6Address);
+        if (r != 0) return r;
 
         if (vlan == null)
             r = o.vlan == null ? 0 : -1;
@@ -326,5 +388,4 @@ public class Entity implements Comparable<Entity> {
 
         return 0;
     }
-    
-}
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/IndexedEntity.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/IndexedEntity.java
index 6fb1dda1305e4e35e86cf95131a2edf17f843895..503b268529c4bfecdb2ce0b6833ed46c3a7dc9cd 100644
--- a/src/main/java/net/floodlightcontroller/devicemanager/internal/IndexedEntity.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/IndexedEntity.java
@@ -18,6 +18,10 @@ package net.floodlightcontroller.devicemanager.internal;
 
 import java.util.EnumSet;
 
+import org.projectfloodlight.openflow.types.DatapathId;
+import org.projectfloodlight.openflow.types.IPv4Address;
+import org.projectfloodlight.openflow.types.IPv6Address;
+import org.projectfloodlight.openflow.types.OFPort;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -51,24 +55,27 @@ public class IndexedEntity {
     }
 
     /**
-     * Check whether this entity has non-null values in any of its key fields
+     * Check whether this entity has non-'zero' values in any of its key fields
      * @return true if any key fields have a non-null value
      */
-    public boolean hasNonNullKeys() {
+    public boolean hasNonZeroOrNonNullKeys() {
         for (DeviceField f : keyFields) {
             switch (f) {
-                case MAC:
+                case MAC: /* We assume operation over Ethernet, thus all devices must have a MAC */
                     return true;
-                case IPV4:
-                    if (entity.ipv4Address != null) return true;
+                case IPv4:
+                    if (!entity.ipv4Address.equals(IPv4Address.NONE)) return true;
+                    break;
+                case IPv6:
+                	if (!entity.ipv6Address.equals(IPv6Address.NONE)) return true;
                     break;
                 case SWITCH:
-                    if (entity.switchDPID != null) return true;
+                    if (!entity.switchDPID.equals(DatapathId.NONE)) return true;
                     break;
                 case PORT:
-                    if (entity.switchPort != null) return true;
+                    if (!entity.switchPort.equals(OFPort.ZERO)) return true;
                     break;
-                case VLAN:
+                case VLAN: /* VLAN can still be null, meaning 'don't care'. VlanVid.ZERO means 'untagged' */
                     if (entity.vlan != null) return true;
                     break;
             }
@@ -92,12 +99,18 @@ public class IndexedEntity {
                         + (int) (entity.macAddress.getLong() ^ 
                                 (entity.macAddress.getLong() >>> 32));
                     break;
-                case IPV4:
+                case IPv4:
                     hashCode = prime * hashCode
                         + ((entity.ipv4Address == null) 
                             ? 0 
                             : entity.ipv4Address.hashCode());
                     break;
+                case IPv6:
+                    hashCode = prime * hashCode
+                        + ((entity.ipv6Address == null) 
+                            ? 0 
+                            : entity.ipv6Address.hashCode());
+                    break;
                 case SWITCH:
                     hashCode = prime * hashCode
                         + ((entity.switchDPID == null) 
@@ -137,11 +150,16 @@ public class IndexedEntity {
                     if (!entity.macAddress.equals(other.entity.macAddress))
                         return false;
                     break;
-                case IPV4:
+                case IPv4:
                     if (entity.ipv4Address == null) {
                         if (other.entity.ipv4Address != null) return false;
                     } else if (!entity.ipv4Address.equals(other.entity.ipv4Address)) return false;
                     break;
+                case IPv6:
+                    if (entity.ipv6Address == null) {
+                        if (other.entity.ipv6Address != null) return false;
+                    } else if (!entity.ipv6Address.equals(other.entity.ipv6Address)) return false;
+                    break;
                 case SWITCH:
                     if (entity.switchDPID == null) {
                         if (other.entity.switchDPID != null) return false;
@@ -158,10 +176,7 @@ public class IndexedEntity {
                     } else if (!entity.vlan.equals(other.entity.vlan)) return false;
                     break;
             }
-        }
-        
+        }  
         return true;
     }
-    
-    
-}
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/web/AbstractDeviceResource.java b/src/main/java/net/floodlightcontroller/devicemanager/web/AbstractDeviceResource.java
index 512e4912babe760a3dbf44049465fd4424f0e96e..595650f45ad385c76c4f5cf49148102a877c9d25 100644
--- a/src/main/java/net/floodlightcontroller/devicemanager/web/AbstractDeviceResource.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/web/AbstractDeviceResource.java
@@ -27,6 +27,7 @@ import net.floodlightcontroller.util.FilterIterator;
 
 import org.projectfloodlight.openflow.types.DatapathId;
 import org.projectfloodlight.openflow.types.IPv4Address;
+import org.projectfloodlight.openflow.types.IPv6Address;
 import org.projectfloodlight.openflow.types.MacAddress;
 import org.projectfloodlight.openflow.types.OFPort;
 import org.projectfloodlight.openflow.types.VlanVid;
@@ -46,6 +47,8 @@ public abstract class AbstractDeviceResource extends ServerResource {
     public static final String IPV4_ERROR = 
             "Invalid IPv4 address: must be in dotted decimal format, " + 
             "234.0.59.1";
+    public static final String IPV6_ERROR = 
+            "Invalid IPv6 address: must be a valid IPv6 format.";
     public static final String DPID_ERROR = 
             "Invalid Switch DPID: must be a 64-bit quantity, expressed in " + 
             "hex as AA:BB:CC:DD:EE:FF:00:11";
@@ -57,16 +60,18 @@ public abstract class AbstractDeviceResource extends ServerResource {
                 (IDeviceService)getContext().getAttributes().
                     get(IDeviceService.class.getCanonicalName());  
                 
-        MacAddress macAddress = null;
-        VlanVid vlan = null;
-        IPv4Address ipv4Address = null;
-        DatapathId switchDPID = null;
-        OFPort switchPort = null;
+        MacAddress macAddress = MacAddress.NONE;
+        VlanVid vlan = null; /* must be null for don't care */
+        IPv4Address ipv4Address = IPv4Address.NONE;
+        IPv6Address ipv6Address = IPv6Address.NONE;
+        DatapathId switchDPID = DatapathId.NONE;
+        OFPort switchPort = OFPort.ZERO;
         
         Form form = getQuery();
         String macAddrStr = form.getFirstValue("mac", true);
         String vlanStr = form.getFirstValue("vlan", true);
         String ipv4Str = form.getFirstValue("ipv4", true);
+        String ipv6Str = form.getFirstValue("ipv6", true);
         String dpid = form.getFirstValue("dpid", true);
         String port = form.getFirstValue("port", true);
         
@@ -98,6 +103,14 @@ public abstract class AbstractDeviceResource extends ServerResource {
                 return null;
             }
         }
+        if (ipv6Str != null) {
+            try {
+                ipv6Address = IPv6Address.of(ipv6Str);
+            } catch (Exception e) {
+                setStatus(Status.CLIENT_ERROR_BAD_REQUEST, IPV6_ERROR);
+                return null;
+            }
+        }
         if (dpid != null) {
             try {
                 switchDPID = DatapathId.of(dpid);
@@ -124,6 +137,7 @@ public abstract class AbstractDeviceResource extends ServerResource {
                 deviceManager.queryDevices(macAddress, 
                                            vlan, 
                                            ipv4Address, 
+                                           ipv6Address,
                                            switchDPID, 
                                            switchPort);
         
@@ -133,6 +147,8 @@ public abstract class AbstractDeviceResource extends ServerResource {
                 form.getFirstValue("vlan__startswith", true);
         final String ipv4StartsWith = 
                 form.getFirstValue("ipv4__startswith", true);
+        final String ipv6StartsWith = 
+                form.getFirstValue("ipv6__startswith", true);
         final String dpidStartsWith = 
                 form.getFirstValue("dpid__startswith", true);
         final String portStartsWith = 
@@ -169,6 +185,19 @@ public abstract class AbstractDeviceResource extends ServerResource {
                     }
                     if (!match) return false;
                 }
+                if (ipv6StartsWith != null) {
+                    boolean match = false;
+                    for (IPv6Address v : value.getIPv6Addresses()) {
+                        String str;
+                        if (v != null && 
+                            (str = v.toString()) != null &&
+                            str.startsWith(ipv6StartsWith)) {
+                            match = true;
+                            break;
+                        }
+                    }
+                    if (!match) return false;
+                }
                 if (dpidStartsWith != null) {
                     boolean match = false;
                     for (SwitchPort v : value.getAttachmentPoints(true)) {
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/web/DeviceSerializer.java b/src/main/java/net/floodlightcontroller/devicemanager/web/DeviceSerializer.java
index e45e438056ea2b6c55e982fbaff106fb57efa9b8..7dca0ea07de36694298ab99abf8a7bdc28e9d5f2 100644
--- a/src/main/java/net/floodlightcontroller/devicemanager/web/DeviceSerializer.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/web/DeviceSerializer.java
@@ -20,6 +20,7 @@ package net.floodlightcontroller.devicemanager.web;
 import java.io.IOException;
 
 import org.projectfloodlight.openflow.types.IPv4Address;
+import org.projectfloodlight.openflow.types.IPv6Address;
 import org.projectfloodlight.openflow.types.VlanVid;
 
 import net.floodlightcontroller.devicemanager.SwitchPort;
@@ -51,6 +52,11 @@ public class DeviceSerializer extends JsonSerializer<Device> {
         for (IPv4Address ip : device.getIPv4Addresses())
             jGen.writeString(ip.toString());
         jGen.writeEndArray();
+        
+        jGen.writeArrayFieldStart("ipv6");
+        for (IPv6Address ip : device.getIPv6Addresses())
+            jGen.writeString(ip.toString());
+        jGen.writeEndArray();
 
         jGen.writeArrayFieldStart("vlan");
         for (VlanVid vlan : device.getVlanId())
diff --git a/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java b/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java
index f347ef87d18ccb33d017d7e46785fa2f9ae926d8..7f07b1a595a9bc7bc6098be628133b81b2adb0f8 100644
--- a/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java
+++ b/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java
@@ -42,6 +42,7 @@ import net.floodlightcontroller.core.util.AppCookie;
 import net.floodlightcontroller.debugcounter.IDebugCounterService;
 import net.floodlightcontroller.packet.Ethernet;
 import net.floodlightcontroller.packet.IPv4;
+import net.floodlightcontroller.packet.IPv6;
 import net.floodlightcontroller.packet.TCP;
 import net.floodlightcontroller.packet.UDP;
 import net.floodlightcontroller.routing.ForwardingBase;
@@ -60,6 +61,7 @@ import org.projectfloodlight.openflow.protocol.OFVersion;
 import org.projectfloodlight.openflow.types.DatapathId;
 import org.projectfloodlight.openflow.types.EthType;
 import org.projectfloodlight.openflow.types.IPv4Address;
+import org.projectfloodlight.openflow.types.IPv6Address;
 import org.projectfloodlight.openflow.types.IpProtocol;
 import org.projectfloodlight.openflow.types.MacAddress;
 import org.projectfloodlight.openflow.types.OFBufferId;
@@ -181,6 +183,8 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule {
 			for (SwitchPort dstDap : dstDevice.getAttachmentPoints()) {
 				DatapathId dstSwDpid = dstDap.getSwitchDPID();
 				DatapathId dstIsland = topologyService.getL2DomainId(dstSwDpid);
+				log.trace("Source Island: {}, Destination Island: {}", srcIsland, dstIsland);
+				log.trace("Source Device: {}, Destination Device: {}", srcDevice, dstDevice);
 				if ((dstIsland != null) && dstIsland.equals(srcIsland)) {
 					on_same_island = true;
 					if (sw.getId().equals(dstSwDpid) && inPort.equals(dstDap.getPort())) {
@@ -337,6 +341,38 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule {
 			}
 		} else if (eth.getEtherType() == EthType.ARP) { /* shallow check for equality is okay for EthType */
 			mb.setExact(MatchField.ETH_TYPE, EthType.ARP);
+		} else if (eth.getEtherType() == EthType.IPv6) {
+			IPv6 ip = (IPv6) eth.getPayload();
+			IPv6Address srcIp = ip.getSourceAddress();
+			IPv6Address dstIp = ip.getDestinationAddress();
+			
+			if (FLOWMOD_DEFAULT_MATCH_IP_ADDR) {
+				mb.setExact(MatchField.ETH_TYPE, EthType.IPv6)
+				.setExact(MatchField.IPV6_SRC, srcIp)
+				.setExact(MatchField.IPV6_DST, dstIp);
+			}
+
+			if (FLOWMOD_DEFAULT_MATCH_TRANSPORT) {
+				/*
+				 * Take care of the ethertype if not included earlier,
+				 * since it's a prerequisite for transport ports.
+				 */
+				if (!FLOWMOD_DEFAULT_MATCH_IP_ADDR) {
+					mb.setExact(MatchField.ETH_TYPE, EthType.IPv6);
+				}
+				
+				if (ip.getNextHeader().equals(IpProtocol.TCP)) {
+					TCP tcp = (TCP) ip.getPayload();
+					mb.setExact(MatchField.IP_PROTO, IpProtocol.TCP)
+					.setExact(MatchField.TCP_SRC, tcp.getSourcePort())
+					.setExact(MatchField.TCP_DST, tcp.getDestinationPort());
+				} else if (ip.getNextHeader().equals(IpProtocol.UDP)) {
+					UDP udp = (UDP) ip.getPayload();
+					mb.setExact(MatchField.IP_PROTO, IpProtocol.UDP)
+					.setExact(MatchField.UDP_SRC, udp.getSourcePort())
+					.setExact(MatchField.UDP_DST, udp.getDestinationPort());
+				}
+			}
 		}
 		return mb.build();
 	}
@@ -470,6 +506,13 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule {
 		} else {
 			log.info("Default priority not configured. Using {}.", FLOWMOD_DEFAULT_PRIORITY);
 		}
+		tmp = configParameters.get("set-send-flow-rem-flag");
+		if (tmp != null) {
+			FLOWMOD_DEFAULT_SET_SEND_FLOW_REM_FLAG = Boolean.parseBoolean(tmp);
+			log.info("Default flags will be set to SEND_FLOW_REM.");
+		} else {
+			log.info("Default flags will be empty.");
+		}
 		tmp = configParameters.get("match");
 		if (tmp != null) {
 			tmp = tmp.toLowerCase();
diff --git a/src/main/java/net/floodlightcontroller/learningswitch/LearningSwitch.java b/src/main/java/net/floodlightcontroller/learningswitch/LearningSwitch.java
index bbc1a01042b4e52cc6c4c2e0ea4c36b5913a07c5..b36262bb6b8e86468b7d0bd8156d0e3ccbd2fab2 100644
--- a/src/main/java/net/floodlightcontroller/learningswitch/LearningSwitch.java
+++ b/src/main/java/net/floodlightcontroller/learningswitch/LearningSwitch.java
@@ -53,6 +53,7 @@ import net.floodlightcontroller.debugcounter.IDebugCounterService;
 import net.floodlightcontroller.debugcounter.IDebugCounterService.MetaData;
 import net.floodlightcontroller.packet.Ethernet;
 import net.floodlightcontroller.restserver.IRestApiService;
+import net.floodlightcontroller.util.OFMessageUtils;
 
 import org.projectfloodlight.openflow.protocol.OFFlowMod;
 import org.projectfloodlight.openflow.protocol.OFFlowRemoved;
@@ -354,27 +355,8 @@ implements IFloodlightModule, ILearningSwitchService, IOFMessageListener {
 	 * @param egressPort The switchport to output the PacketOut.
 	 */
 	private void writePacketOutForPacketIn(IOFSwitch sw, OFPacketIn packetInMessage, OFPort egressPort) {
-		OFPacketOut.Builder pob = sw.getOFFactory().buildPacketOut();
-
-		// Set buffer_id, in_port, actions_len
-		pob.setBufferId(packetInMessage.getBufferId());
-		pob.setInPort(packetInMessage.getVersion().compareTo(OFVersion.OF_12) < 0 ? packetInMessage.getInPort() : packetInMessage.getMatch().get(MatchField.IN_PORT));
-
-		// set actions
-		List<OFAction> actions = new ArrayList<OFAction>(1);
-		actions.add(sw.getOFFactory().actions().buildOutput().setPort(egressPort).setMaxLen(0xffFFffFF).build());
-		pob.setActions(actions);
-
-		// set data - only if buffer_id == -1
-		if (packetInMessage.getBufferId() == OFBufferId.NO_BUFFER) {
-			byte[] packetData = packetInMessage.getData();
-			pob.setData(packetData);
-		}
-
-		// and write it out
+		OFMessageUtils.writePacketOutForPacketIn(sw, packetInMessage, egressPort);
 		counterPacketOut.increment();
-		sw.write(pob.build());
-
 	}
 
 	protected Match createMatchFromPacket(IOFSwitch sw, OFPort inPort, FloodlightContext cntx) {
diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscoveryService.java b/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscoveryService.java
index 2bb53727917ffba3b4138bf3d06a9e56b79db1f2..df179c5952c4c7b738ed17065afe98549c94be87 100644
--- a/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscoveryService.java
+++ b/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscoveryService.java
@@ -25,6 +25,7 @@ import org.projectfloodlight.openflow.types.DatapathId;
 import org.projectfloodlight.openflow.types.MacAddress;
 import org.projectfloodlight.openflow.types.OFPort;
 
+import net.floodlightcontroller.core.IOFSwitch;
 import net.floodlightcontroller.core.module.IFloodlightService;
 import net.floodlightcontroller.routing.Link;
 import net.floodlightcontroller.topology.NodePortTuple;
@@ -62,7 +63,7 @@ public interface ILinkDiscoveryService extends IFloodlightService {
      * to switchport (sw, port). PacketOut does not contain actions.
      * PacketOut length includes the minimum length and data length.
      */
-    public OFPacketOut generateLLDPMessage(DatapathId sw, OFPort port,
+    public OFPacketOut generateLLDPMessage(IOFSwitch iofSwitch, OFPort port,
                                            boolean isStandard,
                                            boolean isReverse);
 
diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java
index ba3eaa081f7576b81e227c688c39f06fce469ff3..4f41f919b0e3f6dda32c12facc0a168a9bc8a55b 100644
--- a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java
+++ b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java
@@ -288,15 +288,14 @@ IFloodlightModule, IInfoProvider {
 	//*********************
 
 	@Override
-	public OFPacketOut generateLLDPMessage(DatapathId sw, OFPort port,
+	public OFPacketOut generateLLDPMessage(IOFSwitch iofSwitch, OFPort port,
 			boolean isStandard, boolean isReverse) {
-
-		IOFSwitch iofSwitch = switchService.getSwitch(sw);
+		
 		OFPortDesc ofpPort = iofSwitch.getPort(port);
 
 		if (log.isTraceEnabled()) {
 			log.trace("Sending LLDP packet out of swich: {}, port: {}",
-					sw.toString(), port);
+					iofSwitch.getId().toString(), port);
 		}
 
 		// using "nearest customer bridge" MAC address for broadest possible
@@ -320,7 +319,7 @@ IFloodlightModule, IInfoProvider {
 		ByteBuffer dpidBB = ByteBuffer.wrap(dpidArray);
 		ByteBuffer portBB = ByteBuffer.wrap(portId, 1, 2);
 
-		DatapathId dpid = sw;
+		DatapathId dpid = iofSwitch.getId();
 		dpidBB.putLong(dpid.getLong());
 		// set the chassis id's value to last 6 bytes of dpid
 		System.arraycopy(dpidArray, 2, chassisId, 1, 6);
@@ -344,7 +343,7 @@ IFloodlightModule, IInfoProvider {
 		portBB.putShort(port.getShortPortNumber());
 		if (log.isTraceEnabled()) {
 			log.trace("Sending LLDP out of interface: {}/{}",
-					sw.toString(), port);
+					iofSwitch.getId().toString(), port);
 		}
 
 		LLDP lldp = new LLDP();
@@ -385,7 +384,7 @@ IFloodlightModule, IInfoProvider {
 
 		// serialize and wrap in a packet out
 		byte[] data = ethernet.serialize();
-		OFPacketOut.Builder pob = switchService.getSwitch(sw).getOFFactory().buildPacketOut();
+		OFPacketOut.Builder pob = iofSwitch.getOFFactory().buildPacketOut();
 		pob.setBufferId(OFBufferId.NO_BUFFER);
 		pob.setInPort(OFPort.ANY);
 
@@ -1160,13 +1159,15 @@ IFloodlightModule, IInfoProvider {
 			return;
 
 		IOFSwitch iofSwitch = switchService.getSwitch(sw);
+		if (iofSwitch == null)             //fix dereference violations in case race conditions
+			return;
 		OFPortDesc ofpPort = iofSwitch.getPort(port);
 
 		if (log.isTraceEnabled()) {
 			log.trace("Sending LLDP packet out of swich: {}, port: {}",
 					sw.toString(), port.getPortNumber());
 		}
-		OFPacketOut po = generateLLDPMessage(sw, port, isStandard, isReverse);
+		OFPacketOut po = generateLLDPMessage(iofSwitch, port, isStandard, isReverse);
 		OFPacketOut.Builder pob = po.createBuilder();
 
 		// Add actions
@@ -1687,6 +1688,8 @@ IFloodlightModule, IInfoProvider {
 	@Override
 	public void switchActivated(DatapathId switchId) {
 		IOFSwitch sw = switchService.getSwitch(switchId);
+		if (sw == null)       //fix dereference violation in case race conditions
+			return;
 		if (sw.getEnabledPortNumbers() != null) {
 			for (OFPort p : sw.getEnabledPortNumbers()) {
 				processNewPort(sw.getId(), p);
diff --git a/src/main/java/net/floodlightcontroller/loadbalancer/LoadBalancer.java b/src/main/java/net/floodlightcontroller/loadbalancer/LoadBalancer.java
index d9c45265cab567837c5dcd1ebc9c1f6d4e48ee69..e691cffae07c9d734b2dea01174d11b2f577141d 100644
--- a/src/main/java/net/floodlightcontroller/loadbalancer/LoadBalancer.java
+++ b/src/main/java/net/floodlightcontroller/loadbalancer/LoadBalancer.java
@@ -190,11 +190,10 @@ public class LoadBalancer implements IFloodlightModule,
                 // retrieve arp to determine target IP address                                                       
                 ARP arpRequest = (ARP) eth.getPayload();
 
-                int targetProtocolAddress = IPv4.toIPv4Address(arpRequest
-                                                               .getTargetProtocolAddress());
+                IPv4Address targetProtocolAddress = arpRequest.getTargetProtocolAddress();
 
-                if (vipIpToId.containsKey(targetProtocolAddress)) {
-                    String vipId = vipIpToId.get(targetProtocolAddress);
+                if (vipIpToId.containsKey(targetProtocolAddress.getInt())) {
+                    String vipId = vipIpToId.get(targetProtocolAddress.getInt());
                     vipProxyArpReply(sw, pi, cntx, vipId);
                     return Command.STOP;
                 }
@@ -227,9 +226,15 @@ public class LoadBalancer implements IFloodlightModule,
                     }
                     
                     LBVip vip = vips.get(vipIpToId.get(destIpAddress));
+                    if (vip == null)			// fix deference violations           
+                    	return Command.CONTINUE;
                     LBPool pool = pools.get(vip.pickPool(client));
+                    if (pool == null)			// fix deference violations
+                    	return Command.CONTINUE;
                     LBMember member = members.get(pool.pickMember(client));
-
+                    if(member == null)			//fix deference violations
+                    	return Command.CONTINUE;
+                    
                     // for chosen member, check device manager and find and push routes, in both directions                    
                     pushBidirectionalVipRoutes(sw, pi, cntx, client, member);
                    
@@ -265,11 +270,10 @@ public class LoadBalancer implements IFloodlightModule,
         ARP arpRequest = (ARP) eth.getPayload();
         
         // have to do proxy arp reply since at this point we cannot determine the requesting application type
-        byte[] vipProxyMacBytes = vips.get(vipId).proxyMac.getBytes();
         
         // generate proxy ARP reply
         IPacket arpReply = new Ethernet()
-            .setSourceMACAddress(vipProxyMacBytes)
+            .setSourceMACAddress(vips.get(vipId).proxyMac)
             .setDestinationMACAddress(eth.getSourceMACAddress())
             .setEtherType(EthType.ARP)
             .setVlanID(eth.getVlanID())
@@ -281,13 +285,10 @@ public class LoadBalancer implements IFloodlightModule,
                 .setHardwareAddressLength((byte) 6)
                 .setProtocolAddressLength((byte) 4)
                 .setOpCode(ARP.OP_REPLY)
-                .setSenderHardwareAddress(vipProxyMacBytes)
-                .setSenderProtocolAddress(
-                        arpRequest.getTargetProtocolAddress())
-                .setTargetHardwareAddress(
-                        eth.getSourceMACAddress().getBytes())
-                .setTargetProtocolAddress(
-                        arpRequest.getSenderProtocolAddress()));
+                .setSenderHardwareAddress(vips.get(vipId).proxyMac)
+                .setSenderProtocolAddress(arpRequest.getTargetProtocolAddress())
+                .setTargetHardwareAddress(eth.getSourceMACAddress())
+                .setTargetProtocolAddress(arpRequest.getSenderProtocolAddress()));
                 
         // push ARP reply out
         pushPacket(arpReply, sw, OFBufferId.NO_BUFFER, OFPort.ANY, (pi.getVersion().compareTo(OFVersion.OF_12) < 0 ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT)), cntx, true);
@@ -667,6 +668,8 @@ public class LoadBalancer implements IFloodlightModule,
         LBPool pool;
         if (pools != null) {
             pool = pools.get(poolId);
+            if (pool == null)	// fix deference violations
+            	return -1;
             if (pool.vipId != null)
                 vips.get(pool.vipId).pools.remove(poolId);
             pools.remove(poolId);
diff --git a/src/main/java/net/floodlightcontroller/packet/ARP.java b/src/main/java/net/floodlightcontroller/packet/ARP.java
index bf3f49385f440f8ed07571c38d341b13ab19e080..48bcd13c9ec1a3436771a5e6abc4ffd4ac7eec77 100644
--- a/src/main/java/net/floodlightcontroller/packet/ARP.java
+++ b/src/main/java/net/floodlightcontroller/packet/ARP.java
@@ -1,327 +1,333 @@
 /**
-*    Copyright 2011, Big Switch Networks, Inc. 
-*    Originally created by David Erickson, Stanford University
-* 
-*    Licensed under the Apache License, Version 2.0 (the "License"); you may
-*    not use this file except in compliance with the License. You may obtain
-*    a copy of the License at
-*
-*         http://www.apache.org/licenses/LICENSE-2.0
-*
-*    Unless required by applicable law or agreed to in writing, software
-*    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-*    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-*    License for the specific language governing permissions and limitations
-*    under the License.
-**/
+ *    Copyright 2011, Big Switch Networks, Inc. 
+ *    Originally created by David Erickson, Stanford University
+ * 
+ *    Licensed under the Apache License, Version 2.0 (the "License"); you may
+ *    not use this file except in compliance with the License. You may obtain
+ *    a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ *    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ *    License for the specific language governing permissions and limitations
+ *    under the License.
+ **/
 
 package net.floodlightcontroller.packet;
 
 import java.nio.ByteBuffer;
-import java.util.Arrays;
+
+import org.projectfloodlight.openflow.types.ArpOpcode;
+import org.projectfloodlight.openflow.types.IPv4Address;
+import org.projectfloodlight.openflow.types.MacAddress;
 
 /**
  *
  * @author David Erickson (daviderickson@cs.stanford.edu)
  */
 public class ARP extends BasePacket {
-    public static short HW_TYPE_ETHERNET = 0x1;
-
-    public static short PROTO_TYPE_IP = 0x800;
-
-    public static short OP_REQUEST = 0x1;
-    public static short OP_REPLY = 0x2;
-    public static short OP_RARP_REQUEST = 0x3;
-    public static short OP_RARP_REPLY = 0x4;
-
-    protected short hardwareType;
-    protected short protocolType;
-    protected byte hardwareAddressLength;
-    protected byte protocolAddressLength;
-    protected short opCode;
-    protected byte[] senderHardwareAddress;
-    protected byte[] senderProtocolAddress;
-    protected byte[] targetHardwareAddress;
-    protected byte[] targetProtocolAddress;
-
-    /**
-     * @return the hardwareType
-     */
-    public short getHardwareType() {
-        return hardwareType;
-    }
-
-    /**
-     * @param hardwareType the hardwareType to set
-     */
-    public ARP setHardwareType(short hardwareType) {
-        this.hardwareType = hardwareType;
-        return this;
-    }
-
-    /**
-     * @return the protocolType
-     */
-    public short getProtocolType() {
-        return protocolType;
-    }
-
-    /**
-     * @param protocolType the protocolType to set
-     */
-    public ARP setProtocolType(short protocolType) {
-        this.protocolType = protocolType;
-        return this;
-    }
-
-    /**
-     * @return the hardwareAddressLength
-     */
-    public byte getHardwareAddressLength() {
-        return hardwareAddressLength;
-    }
-
-    /**
-     * @param hardwareAddressLength the hardwareAddressLength to set
-     */
-    public ARP setHardwareAddressLength(byte hardwareAddressLength) {
-        this.hardwareAddressLength = hardwareAddressLength;
-        return this;
-    }
-
-    /**
-     * @return the protocolAddressLength
-     */
-    public byte getProtocolAddressLength() {
-        return protocolAddressLength;
-    }
-
-    /**
-     * @param protocolAddressLength the protocolAddressLength to set
-     */
-    public ARP setProtocolAddressLength(byte protocolAddressLength) {
-        this.protocolAddressLength = protocolAddressLength;
-        return this;
-    }
-
-    /**
-     * @return the opCode
-     */
-    public short getOpCode() {
-        return opCode;
-    }
-
-    /**
-     * @param opCode the opCode to set
-     */
-    public ARP setOpCode(short opCode) {
-        this.opCode = opCode;
-        return this;
-    }
-
-    /**
-     * @return the senderHardwareAddress
-     */
-    public byte[] getSenderHardwareAddress() {
-        return senderHardwareAddress;
-    }
-
-    /**
-     * @param senderHardwareAddress the senderHardwareAddress to set
-     */
-    public ARP setSenderHardwareAddress(byte[] senderHardwareAddress) {
-        this.senderHardwareAddress = senderHardwareAddress;
-        return this;
-    }
-
-    /**
-     * @return the senderProtocolAddress
-     */
-    public byte[] getSenderProtocolAddress() {
-        return senderProtocolAddress;
-    }
-
-    /**
-     * @param senderProtocolAddress the senderProtocolAddress to set
-     */
-    public ARP setSenderProtocolAddress(byte[] senderProtocolAddress) {
-        this.senderProtocolAddress = senderProtocolAddress;
-        return this;
-    }
-    
-    public ARP setSenderProtocolAddress(int address) {
-        this.senderProtocolAddress = ByteBuffer.allocate(4).putInt(address).array();
-        return this;
-    }
-
-    /**
-     * @return the targetHardwareAddress
-     */
-    public byte[] getTargetHardwareAddress() {
-        return targetHardwareAddress;
-    }
-
-    /**
-     * @param targetHardwareAddress the targetHardwareAddress to set
-     */
-    public ARP setTargetHardwareAddress(byte[] targetHardwareAddress) {
-        this.targetHardwareAddress = targetHardwareAddress;
-        return this;
-    }
-
-    /**
-     * @return the targetProtocolAddress
-     */
-    public byte[] getTargetProtocolAddress() {
-        return targetProtocolAddress;
-    }
-
-    /**
-     * @return True if gratuitous ARP (SPA = TPA), false otherwise
-     */
-    public boolean isGratuitous() {        
-        assert(senderProtocolAddress.length == targetProtocolAddress.length);
-        
-        int indx = 0;
-        while (indx < senderProtocolAddress.length) {
-            if (senderProtocolAddress[indx] != targetProtocolAddress[indx]) {
-                return false;
-            }
-            indx++;
-        }
-        
-        return true;
-    }
-    
-    /**
-     * @param targetProtocolAddress the targetProtocolAddress to set
-     */
-    public ARP setTargetProtocolAddress(byte[] targetProtocolAddress) {
-        this.targetProtocolAddress = targetProtocolAddress;
-        return this;
-    }
-    
-    public ARP setTargetProtocolAddress(int address) {
-        this.targetProtocolAddress = ByteBuffer.allocate(4).putInt(address).array();
-        return this;
-    }
-
-    @Override
-    public byte[] serialize() {
-        int length = 8 + (2 * (0xff & this.hardwareAddressLength))
-                + (2 * (0xff & this.protocolAddressLength));
-        byte[] data = new byte[length];
-        ByteBuffer bb = ByteBuffer.wrap(data);
-        bb.putShort(this.hardwareType);
-        bb.putShort(this.protocolType);
-        bb.put(this.hardwareAddressLength);
-        bb.put(this.protocolAddressLength);
-        bb.putShort(this.opCode);
-        bb.put(this.senderHardwareAddress, 0, 0xff & this.hardwareAddressLength);
-        bb.put(this.senderProtocolAddress, 0, 0xff & this.protocolAddressLength);
-        bb.put(this.targetHardwareAddress, 0, 0xff & this.hardwareAddressLength);
-        bb.put(this.targetProtocolAddress, 0, 0xff & this.protocolAddressLength);
-        return data;
-    }
-
-    @Override
-    public IPacket deserialize(byte[] data, int offset, int length)
-            throws PacketParsingException {
-        ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
-        this.hardwareType = bb.getShort();
-        this.protocolType = bb.getShort();
-        this.hardwareAddressLength = bb.get();
-        this.protocolAddressLength = bb.get();
-        if (this.hardwareAddressLength != 6) {
-            String msg = "Incorrect ARP hardware address length: " +
-                        hardwareAddressLength;
-            throw new PacketParsingException(msg);
-        }
-        if (this.protocolAddressLength != 4) {
-            String msg = "Incorrect ARP protocol address length: " +
-                        protocolAddressLength;
-            throw new PacketParsingException(msg);
-        }
-        this.opCode = bb.getShort();
-        this.senderHardwareAddress = new byte[0xff & this.hardwareAddressLength];
-        bb.get(this.senderHardwareAddress, 0, this.senderHardwareAddress.length);
-        this.senderProtocolAddress = new byte[0xff & this.protocolAddressLength];
-        bb.get(this.senderProtocolAddress, 0, this.senderProtocolAddress.length);
-        this.targetHardwareAddress = new byte[0xff & this.hardwareAddressLength];
-        bb.get(this.targetHardwareAddress, 0, this.targetHardwareAddress.length);
-        this.targetProtocolAddress = new byte[0xff & this.protocolAddressLength];
-        bb.get(this.targetProtocolAddress, 0, this.targetProtocolAddress.length);
-        return this;
-    }
-
-    /* (non-Javadoc)
-     * @see java.lang.Object#hashCode()
-     */
-    @Override
-    public int hashCode() {
-        final int prime = 13121;
-        int result = super.hashCode();
-        result = prime * result + hardwareAddressLength;
-        result = prime * result + hardwareType;
-        result = prime * result + opCode;
-        result = prime * result + protocolAddressLength;
-        result = prime * result + protocolType;
-        result = prime * result + Arrays.hashCode(senderHardwareAddress);
-        result = prime * result + Arrays.hashCode(senderProtocolAddress);
-        result = prime * result + Arrays.hashCode(targetHardwareAddress);
-        result = prime * result + Arrays.hashCode(targetProtocolAddress);
-        return result;
-    }
-
-    /* (non-Javadoc)
-     * @see java.lang.Object#equals(java.lang.Object)
-     */
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj)
-            return true;
-        if (!super.equals(obj))
-            return false;
-        if (!(obj instanceof ARP))
-            return false;
-        ARP other = (ARP) obj;
-        if (hardwareAddressLength != other.hardwareAddressLength)
-            return false;
-        if (hardwareType != other.hardwareType)
-            return false;
-        if (opCode != other.opCode)
-            return false;
-        if (protocolAddressLength != other.protocolAddressLength)
-            return false;
-        if (protocolType != other.protocolType)
-            return false;
-        if (!Arrays.equals(senderHardwareAddress, other.senderHardwareAddress))
-            return false;
-        if (!Arrays.equals(senderProtocolAddress, other.senderProtocolAddress))
-            return false;
-        if (!Arrays.equals(targetHardwareAddress, other.targetHardwareAddress))
-            return false;
-        if (!Arrays.equals(targetProtocolAddress, other.targetProtocolAddress))
-            return false;
-        return true;
-    }
-
-    /* (non-Javadoc)
-     * @see java.lang.Object#toString()
-     */
-    @Override
-    public String toString() {
-        return "ARP [hardwareType=" + hardwareType + ", protocolType="
-                + protocolType + ", hardwareAddressLength="
-                + hardwareAddressLength + ", protocolAddressLength="
-                + protocolAddressLength + ", opCode=" + opCode
-                + ", senderHardwareAddress="
-                + Arrays.toString(senderHardwareAddress)
-                + ", senderProtocolAddress="
-                + Arrays.toString(senderProtocolAddress)
-                + ", targetHardwareAddress="
-                + Arrays.toString(targetHardwareAddress)
-                + ", targetProtocolAddress="
-                + Arrays.toString(targetProtocolAddress) + "]";
-    }
+	public static short HW_TYPE_ETHERNET = 0x1;
+
+	public static short PROTO_TYPE_IP = 0x800;
+
+	public static ArpOpcode OP_REQUEST = ArpOpcode.REQUEST;
+	public static ArpOpcode OP_REPLY = ArpOpcode.REPLY;
+	public static ArpOpcode OP_RARP_REQUEST = ArpOpcode.REQUEST_REVERSE;
+	public static ArpOpcode OP_RARP_REPLY = ArpOpcode.REPLY_REVERSE;
+
+	protected short hardwareType;
+	protected short protocolType;
+	protected byte hardwareAddressLength;
+	protected byte protocolAddressLength;
+	protected ArpOpcode opCode;
+	protected MacAddress senderHardwareAddress;
+	protected IPv4Address senderProtocolAddress;
+	protected MacAddress targetHardwareAddress;
+	protected IPv4Address targetProtocolAddress;
+
+	/**
+	 * @return the hardwareType
+	 */
+	public short getHardwareType() {
+		return hardwareType;
+	}
+
+	/**
+	 * @param hardwareType the hardwareType to set
+	 */
+	public ARP setHardwareType(short hardwareType) {
+		this.hardwareType = hardwareType;
+		return this;
+	}
+
+	/**
+	 * @return the protocolType
+	 */
+	public short getProtocolType() {
+		return protocolType;
+	}
+
+	/**
+	 * @param protocolType the protocolType to set
+	 */
+	public ARP setProtocolType(short protocolType) {
+		this.protocolType = protocolType;
+		return this;
+	}
+
+	/**
+	 * @return the hardwareAddressLength
+	 */
+	public byte getHardwareAddressLength() {
+		return hardwareAddressLength;
+	}
+
+	/**
+	 * @param hardwareAddressLength the hardwareAddressLength to set
+	 */
+	public ARP setHardwareAddressLength(byte hardwareAddressLength) {
+		this.hardwareAddressLength = hardwareAddressLength;
+		return this;
+	}
+
+	/**
+	 * @return the protocolAddressLength
+	 */
+	public byte getProtocolAddressLength() {
+		return protocolAddressLength;
+	}
+
+	/**
+	 * @param protocolAddressLength the protocolAddressLength to set
+	 */
+	public ARP setProtocolAddressLength(byte protocolAddressLength) {
+		this.protocolAddressLength = protocolAddressLength;
+		return this;
+	}
+
+	/**
+	 * @return the opCode
+	 */
+	public ArpOpcode getOpCode() {
+		return opCode;
+	}
+
+	/**
+	 * @param opCode the opCode to set
+	 */
+	public ARP setOpCode(ArpOpcode opCode) {
+		this.opCode = opCode;
+		return this;
+	}
+
+	/**
+	 * @return the senderHardwareAddress
+	 */
+	public MacAddress getSenderHardwareAddress() {
+		return senderHardwareAddress;
+	}
+
+	/**
+	 * @param senderHardwareAddress the senderHardwareAddress to set
+	 */
+	public ARP setSenderHardwareAddress(MacAddress senderHardwareAddress) {
+		this.senderHardwareAddress = senderHardwareAddress;
+		return this;
+	}
+
+	/**
+	 * @return the senderProtocolAddress
+	 */
+	public IPv4Address getSenderProtocolAddress() {
+		return senderProtocolAddress;
+	}
+
+	/**
+	 * @param senderProtocolAddress the senderProtocolAddress to set
+	 */
+	public ARP setSenderProtocolAddress(IPv4Address senderProtocolAddress) {
+		this.senderProtocolAddress = senderProtocolAddress;
+		return this;
+	}
+
+	/**
+	 * @return the targetHardwareAddress
+	 */
+	public MacAddress getTargetHardwareAddress() {
+		return targetHardwareAddress;
+	}
+
+	/**
+	 * @param targetHardwareAddress the targetHardwareAddress to set
+	 */
+	public ARP setTargetHardwareAddress(MacAddress targetHardwareAddress) {
+		this.targetHardwareAddress = targetHardwareAddress;
+		return this;
+	}
+
+	/**
+	 * @return the targetProtocolAddress
+	 */
+	public IPv4Address getTargetProtocolAddress() {
+		return targetProtocolAddress;
+	}
+
+	/**
+	 * @return True if gratuitous ARP (SPA = TPA), false otherwise
+	 */
+	public boolean isGratuitous() {        
+		assert(senderProtocolAddress.getLength() == targetProtocolAddress.getLength());
+
+		return senderProtocolAddress.equals(targetProtocolAddress);
+	}
+
+	/**
+	 * @param targetProtocolAddress the targetProtocolAddress to set
+	 */
+	public ARP setTargetProtocolAddress(IPv4Address targetProtocolAddress) {
+		this.targetProtocolAddress = targetProtocolAddress;
+		return this;
+	}
+
+	@Override
+	public byte[] serialize() {
+		int length = 8 + (2 * (0xff & this.hardwareAddressLength))
+				+ (2 * (0xff & this.protocolAddressLength));
+		byte[] data = new byte[length];
+		ByteBuffer bb = ByteBuffer.wrap(data);
+		bb.putShort(this.hardwareType);
+		bb.putShort(this.protocolType);
+		bb.put(this.hardwareAddressLength);
+		bb.put(this.protocolAddressLength);
+		bb.putShort((short) this.opCode.getOpcode());
+		bb.put(this.senderHardwareAddress.getBytes());
+		bb.put(this.senderProtocolAddress.getBytes());
+		bb.put(this.targetHardwareAddress.getBytes());
+		bb.put(this.targetProtocolAddress.getBytes());
+		return data;
+	}
+
+	@Override
+	public IPacket deserialize(byte[] data, int offset, int length)
+			throws PacketParsingException {
+		ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+		this.hardwareType = bb.getShort();
+		this.protocolType = bb.getShort();
+		this.hardwareAddressLength = bb.get();
+		this.protocolAddressLength = bb.get();
+		if (this.hardwareAddressLength != 6) {
+			String msg = "Incorrect ARP hardware address length: " +
+					hardwareAddressLength;
+			throw new PacketParsingException(msg);
+		}
+		if (this.protocolAddressLength != 4) {
+			String msg = "Incorrect ARP protocol address length: " +
+					protocolAddressLength;
+			throw new PacketParsingException(msg);
+		}
+		this.opCode = ArpOpcode.of(bb.getShort());
+
+		byte[] tmpMac = new byte[0xff & this.hardwareAddressLength];
+		byte[] tmpIp = new byte[0xff & this.protocolAddressLength];
+
+		bb.get(tmpMac, 0, this.hardwareAddressLength);
+		this.senderHardwareAddress = MacAddress.of(tmpMac);  
+		bb.get(tmpIp, 0, this.protocolAddressLength);
+		this.senderProtocolAddress = IPv4Address.of(tmpIp);
+
+		bb.get(tmpMac, 0, this.hardwareAddressLength);
+		this.targetHardwareAddress = MacAddress.of(tmpMac);  
+		bb.get(tmpIp, 0, this.protocolAddressLength);
+		this.targetProtocolAddress = IPv4Address.of(tmpIp);
+
+		return this;
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = super.hashCode();
+		result = prime * result + hardwareAddressLength;
+		result = prime * result + hardwareType;
+		result = prime * result + ((opCode == null) ? 0 : opCode.hashCode());
+		result = prime * result + protocolAddressLength;
+		result = prime * result + protocolType;
+		result = prime
+				* result
+				+ ((senderHardwareAddress == null) ? 0 : senderHardwareAddress
+						.hashCode());
+		result = prime
+				* result
+				+ ((senderProtocolAddress == null) ? 0 : senderProtocolAddress
+						.hashCode());
+		result = prime
+				* result
+				+ ((targetHardwareAddress == null) ? 0 : targetHardwareAddress
+						.hashCode());
+		result = prime
+				* result
+				+ ((targetProtocolAddress == null) ? 0 : targetProtocolAddress
+						.hashCode());
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (!super.equals(obj))
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		ARP other = (ARP) obj;
+		if (hardwareAddressLength != other.hardwareAddressLength)
+			return false;
+		if (hardwareType != other.hardwareType)
+			return false;
+		if (opCode == null) {
+			if (other.opCode != null)
+				return false;
+		} else if (!opCode.equals(other.opCode))
+			return false;
+		if (protocolAddressLength != other.protocolAddressLength)
+			return false;
+		if (protocolType != other.protocolType)
+			return false;
+		if (senderHardwareAddress == null) {
+			if (other.senderHardwareAddress != null)
+				return false;
+		} else if (!senderHardwareAddress.equals(other.senderHardwareAddress))
+			return false;
+		if (senderProtocolAddress == null) {
+			if (other.senderProtocolAddress != null)
+				return false;
+		} else if (!senderProtocolAddress.equals(other.senderProtocolAddress))
+			return false;
+		if (targetHardwareAddress == null) {
+			if (other.targetHardwareAddress != null)
+				return false;
+		} else if (!targetHardwareAddress.equals(other.targetHardwareAddress))
+			return false;
+		if (targetProtocolAddress == null) {
+			if (other.targetProtocolAddress != null)
+				return false;
+		} else if (!targetProtocolAddress.equals(other.targetProtocolAddress))
+			return false;
+		return true;
+	}
+
+	@Override
+	public String toString() {
+		return "ARP [hardwareType=" + hardwareType + ", protocolType="
+				+ protocolType + ", hardwareAddressLength="
+				+ hardwareAddressLength + ", protocolAddressLength="
+				+ protocolAddressLength + ", opCode=" + opCode
+				+ ", senderHardwareAddress=" + senderHardwareAddress
+				+ ", senderProtocolAddress=" + senderProtocolAddress
+				+ ", targetHardwareAddress=" + targetHardwareAddress
+				+ ", targetProtocolAddress=" + targetProtocolAddress + "]";
+	}
+
 }
diff --git a/src/main/java/net/floodlightcontroller/packet/Ethernet.java b/src/main/java/net/floodlightcontroller/packet/Ethernet.java
index 71ef21907ceb462288c4e1634bb9c9d9af1475ad..b3ef406e69298fc785e975cb488fb3833de5d36b 100644
--- a/src/main/java/net/floodlightcontroller/packet/Ethernet.java
+++ b/src/main/java/net/floodlightcontroller/packet/Ethernet.java
@@ -1,7 +1,7 @@
 /**
-*    Copyright 2011, Big Switch Networks, Inc. 
+*    Copyright 2011, Big Switch Networks, Inc.
 *    Originally created by David Erickson, Stanford University
-* 
+*
 *    Licensed under the Apache License, Version 2.0 (the "License"); you may
 *    not use this file except in compliance with the License. You may obtain
 *    a copy of the License at
@@ -35,6 +35,7 @@ public class Ethernet extends BasePacket {
     public static final short TYPE_ARP = 0x0806;
     public static final short TYPE_RARP = (short) 0x8035;
     public static final short TYPE_IPv4 = 0x0800;
+    public static final short TYPE_IPv6 = (short) 0x86DD;
     public static final short TYPE_LLDP = (short) 0x88cc;
     public static final short TYPE_BSN = (short) 0x8942;
     public static final short VLAN_UNTAGGED = VlanVid.ZERO.getVlan(); // untagged vlan must be 0x0000 for loxi. We can use the convenient ZERO field
@@ -46,6 +47,7 @@ public class Ethernet extends BasePacket {
         etherTypeClassMap.put(TYPE_ARP, ARP.class);
         etherTypeClassMap.put(TYPE_RARP, ARP.class);
         etherTypeClassMap.put(TYPE_IPv4, IPv4.class);
+        etherTypeClassMap.put(TYPE_IPv6, IPv6.class);
         etherTypeClassMap.put(TYPE_LLDP, LLDP.class);
         etherTypeClassMap.put(TYPE_BSN, BSN.class);
     }
@@ -64,7 +66,7 @@ public class Ethernet extends BasePacket {
         super();
         this.vlanID = VLAN_UNTAGGED;
     }
-    
+
     /**
      * @return the destination MAC
      */
@@ -79,7 +81,7 @@ public class Ethernet extends BasePacket {
         this.destinationMACAddress = MacAddress.of(destinationMACAddress);
         return this;
     }
-    
+
     /**
      * @param destinationMACAddress the destination MAC to set
      */
@@ -110,7 +112,7 @@ public class Ethernet extends BasePacket {
         this.sourceMACAddress = MacAddress.of(sourceMACAddress);
         return this;
     }
-    
+
     /**
      * @param sourceMACAddress the source MAC to set
      */
@@ -171,7 +173,7 @@ public class Ethernet extends BasePacket {
         this.etherType = etherType;
         return this;
     }
-    
+
     /**
      * @return True if the Ethernet frame is broadcast, false otherwise
      */
@@ -179,7 +181,7 @@ public class Ethernet extends BasePacket {
         assert(destinationMACAddress.getLength() == 6);
         return destinationMACAddress.isBroadcast();
     }
-    
+
     /**
      * @return True is the Ethernet frame is multicast, False otherwise
      */
@@ -248,9 +250,9 @@ public class Ethernet extends BasePacket {
         bb.get(srcAddr);
         this.sourceMACAddress = MacAddress.of(srcAddr);
 
-        /* 
+        /*
          * The ethertype is represented as 2 bytes in the packet header;
-         * however, EthType can only parse an int. Negative shorts (1 in 
+         * however, EthType can only parse an int. Negative shorts (1 in
          * the most sig place b/c 2's complement) are still valid ethertypes,
          * but it doesn't appear this way unless we treat the sign bit as
          * part of an unsigned number. If we natively cast the short to an
@@ -269,7 +271,7 @@ public class Ethernet extends BasePacket {
             this.vlanID = VLAN_UNTAGGED;
         }
         this.etherType = etherType;
-        
+
         IPacket payload;
         if (Ethernet.etherTypeClassMap.containsKey((short) this.etherType.getValue())) {
             Class<? extends IPacket> clazz = Ethernet.etherTypeClassMap.get((short) this.etherType.getValue());
@@ -321,7 +323,7 @@ public class Ethernet extends BasePacket {
         if (macBytes.length != 6)
             return false;
         for (int i = 0; i < 6; ++i) {
-            if (HEXES.indexOf(macBytes[i].toUpperCase().charAt(0)) == -1 || 
+            if (HEXES.indexOf(macBytes[i].toUpperCase().charAt(0)) == -1 ||
                 HEXES.indexOf(macBytes[i].toUpperCase().charAt(1)) == -1) {
                 return false;
             }
@@ -333,7 +335,7 @@ public class Ethernet extends BasePacket {
      * Accepts a MAC address of the form 00:aa:11:bb:22:cc, case does not
      * matter, and returns a corresponding byte[].
      * @param macAddress The MAC address to convert into a bye array
-     * @return The macAddress as a byte array 
+     * @return The macAddress as a byte array
      */
     public static byte[] toMACAddress(String macAddress) {
         return MacAddress.of(macAddress).getBytes();
@@ -358,7 +360,7 @@ public class Ethernet extends BasePacket {
     public static byte[] toByteArray(long macAddress) {
         return MacAddress.of(macAddress).getBytes();
     }
-    
+
     @Override
 	public int hashCode() {
 		final int prime = 31;
@@ -410,7 +412,7 @@ public class Ethernet extends BasePacket {
 			return false;
 		return true;
 	}
-    
+
     /* (non-Javadoc)
      * @see java.lang.Object#toString(java.lang.Object)
      */
@@ -449,9 +451,9 @@ public class Ethernet extends BasePacket {
         if (pkt instanceof ARP) {
             ARP p = (ARP) pkt;
             sb.append("\nnw_src: ");
-            sb.append(IPv4.fromIPv4Address(IPv4.toIPv4Address(p.getSenderProtocolAddress())));
+            sb.append(p.getSenderProtocolAddress().toString());
             sb.append("\nnw_dst: ");
-            sb.append(IPv4.fromIPv4Address(IPv4.toIPv4Address(p.getTargetProtocolAddress())));
+            sb.append(p.getTargetProtocolAddress().toString());
         }
         else if (pkt instanceof LLDP) {
             sb.append("lldp packet");
@@ -474,6 +476,17 @@ public class Ethernet extends BasePacket {
             sb.append("\nnw_proto: ");
             sb.append(p.getProtocol());
         }
+        else if (pkt instanceof IPv6) {
+        	IPv6 p = (IPv6) pkt;
+        	sb.append("\nnw_src: ");
+            sb.append(p.getSourceAddress().toString());
+            sb.append("\nnw_dst: ");
+            sb.append(p.getDestinationAddress().toString());
+            sb.append("\nnw_tclass: ");
+            sb.append(p.getTrafficClass());
+            sb.append("\nnw_proto: ");
+            sb.append(p.getNextHeader().toString());
+        }
         else if (pkt instanceof DHCP) {
             sb.append("\ndhcp packet");
         }
@@ -486,9 +499,8 @@ public class Ethernet extends BasePacket {
         else if (pkt instanceof BPDU) {
             sb.append("\nbpdu packet");
         }
-        else sb.append("\nunknwon packet");
+        else sb.append("\nunknown packet");
 
         return sb.toString();
     }
-
-}
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/packet/IPv6.java b/src/main/java/net/floodlightcontroller/packet/IPv6.java
new file mode 100644
index 0000000000000000000000000000000000000000..e15b45e024fd9a4ecdf6aa9f9e1915a457ab8e5a
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/packet/IPv6.java
@@ -0,0 +1,304 @@
+/**
+ *    Copyright 2015, Big Switch Networks, Inc.
+ *    Originally created by David Erickson, Stanford University
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License"); you may
+ *    not use this file except in compliance with the License. You may obtain
+ *    a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ *    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ *    License for the specific language governing permissions and limitations
+ *    under the License.
+ **/
+package net.floodlightcontroller.packet;
+
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.projectfloodlight.openflow.types.IPv6Address;
+import org.projectfloodlight.openflow.types.IpProtocol;
+
+/**
+ * @author Jacob Chappell (jacob.chappell@uky.edu)
+ * @edited Ryan Izard, ryan.izard@bigswitch.com, rizard@g.clemson.edu
+ */
+public class IPv6 extends BasePacket {
+	public static Map<IpProtocol, Class<? extends IPacket>> nextHeaderClassMap;
+
+	static {
+		nextHeaderClassMap = new HashMap<IpProtocol, Class<? extends IPacket>>();
+		// TODO: Add ICMPv6, IPv6 Options, etc..
+		nextHeaderClassMap.put(IpProtocol.TCP, TCP.class);
+		nextHeaderClassMap.put(IpProtocol.UDP, UDP.class);
+	}
+
+	public static final int HEADER_LENGTH = 40;
+
+	protected byte version;
+	protected byte trafficClass;
+	protected int flowLabel;
+	protected short payloadLength;
+	protected IpProtocol nextHeader;
+	protected byte hopLimit;
+	protected IPv6Address sourceAddress;
+	protected IPv6Address destinationAddress;
+
+	public IPv6() {
+		super();
+		this.version = 6;
+		nextHeader = IpProtocol.NONE;
+		sourceAddress = IPv6Address.NONE;
+		destinationAddress = IPv6Address.NONE;
+	}
+
+	public byte getVersion() {
+		return version;
+	}
+
+	public IPv6 setVersion(byte version) {
+		this.version = version;
+		return this;
+	}
+
+	public byte getTrafficClass() {
+		return trafficClass;
+	}
+
+	public IPv6 setTrafficClass(byte trafficClass) {
+		this.trafficClass = trafficClass;
+		return this;
+	}
+
+	public int getFlowLabel() {
+		return flowLabel;
+	}
+
+	public IPv6 setFlowLabel(int flowLabel) {
+		this.flowLabel = flowLabel;
+		return this;
+	}
+
+	public short getPayloadLength() {
+		return payloadLength;
+	}
+
+	public IPv6 setPayloadLength(short payloadLength) {
+		this.payloadLength = payloadLength;
+		return this;
+	}
+
+	public IpProtocol getNextHeader() {
+		return nextHeader;
+	}
+
+	public IPv6 setNextHeader(IpProtocol nextHeader) {
+		this.nextHeader = nextHeader;
+		return this;
+	}
+
+	public byte getHopLimit() {
+		return hopLimit;
+	}
+
+	public IPv6 setHopLimit(byte hopLimit) {
+		this.hopLimit = hopLimit;
+		return this;
+	}
+
+	public IPv6Address getSourceAddress() {
+		return sourceAddress;
+	}
+
+	public IPv6 setSourceAddress(IPv6Address sourceAddress) {
+		this.sourceAddress = sourceAddress;
+		return this;
+	}
+
+	public IPv6Address getDestinationAddress() {
+		return destinationAddress;
+	}
+
+	public IPv6 setDestinationAddress(IPv6Address destinationAddress) {
+		this.destinationAddress = destinationAddress;
+		return this;
+	}
+
+	@Override
+	public byte[] serialize() {
+		// Get the raw bytes of the payload we encapsulate.
+		byte[] payloadData = null;
+		if (this.payload != null) {
+			this.payload.setParent(this);
+			payloadData = this.payload.serialize();
+			/* 
+			 * If we forgot to include the IpProtocol before serializing, 
+			 * try to ascertain what it is from the payload. If it's not
+			 * a payload type we know about, we'll throw an exception.
+			 */
+			if ((this.nextHeader == null || this.nextHeader.equals(IpProtocol.NONE))) {
+				if (IPv6.nextHeaderClassMap.containsValue(this.payload.getClass())) {
+					Set<Map.Entry<IpProtocol, Class<? extends IPacket>>> entries = IPv6.nextHeaderClassMap.entrySet();
+					for (Map.Entry<IpProtocol, Class<? extends IPacket>> m : entries) {
+						if (m.getValue().equals(this.payload.getClass())) {            		
+							this.setNextHeader(m.getKey());
+							log.warn("Setting previously unset IPv6 'next header' to {} as detected by payload {}", m.getKey(), this.getPayload().getClass().toString());
+							break;
+						}
+					}
+				} else if (this.payload instanceof Data) {
+					/* we're good -- there shouldn't be an IpProtocol set it it's just data */
+				} else {
+					throw new IllegalArgumentException("IpProtocol is unset in IPv6 packet " + this.toString() + ". Unable to determine payload type to set for payload " + this.getPayload().getClass().toString());
+				}
+			}
+		}
+		// Update our internal payload length.
+		this.payloadLength = (short) ((payloadData != null) ? payloadData.length : 0);
+		// Create a byte buffer to hold the IPv6 packet structure.
+		byte[] data = new byte[HEADER_LENGTH + this.payloadLength];
+		ByteBuffer bb = ByteBuffer.wrap(data);
+		// Add header fields to the byte buffer in the correct order.
+		// Fear not the bit magic that must occur.
+		bb.put((byte) (((this.version & 0xF) << 4) |
+				((this.trafficClass & 0xF0) >>> 4)));
+		bb.put((byte) (((this.trafficClass & 0xF) << 4) |
+				((this.flowLabel & 0xF0000) >>> 16)));
+		bb.putShort((short) (this.flowLabel & 0xFFFF));
+		bb.putShort(this.payloadLength);
+		bb.put((byte) this.nextHeader.getIpProtocolNumber());
+		bb.put(this.hopLimit);
+		bb.put(this.sourceAddress.getBytes());
+		bb.put(this.destinationAddress.getBytes());
+		// Add the payload to the byte buffer, if necessary.
+		if (payloadData != null)
+			bb.put(payloadData);
+		// We're done! Return the data.
+		return data;
+	}
+
+	@Override
+	public String toString() {
+		return "IPv6 [version=" + version + ", trafficClass=" + trafficClass
+				+ ", flowLabel=" + flowLabel + ", payloadLength="
+				+ payloadLength + ", nextHeader=" + nextHeader + ", hopLimit="
+				+ hopLimit + ", sourceAddress=" + sourceAddress
+				+ ", destinationAddress=" + destinationAddress + ", parent="
+				+ parent + ", payload=" + payload + "]";
+	}
+
+	@Override
+	public IPacket deserialize(byte[] data, int offset, int length)
+			throws PacketParsingException {
+		// Wrap the data in a byte buffer for easier retrieval.
+		ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+		// Retrieve values from IPv6 header.
+		byte firstByte = bb.get();
+		byte secondByte = bb.get();
+		this.version = (byte) ((firstByte & 0xF0) >>> 4);
+		if (this.version != 6) {
+			throw new PacketParsingException(
+					"Invalid version for IPv6 packet: " +
+							this.version);
+		}
+		this.trafficClass = (byte) (((firstByte & 0xF) << 4) |
+				((secondByte & 0xF0) >>> 4));
+		this.flowLabel = ((secondByte & 0xF) << 16) |
+				(bb.getShort() & 0xFFFF);
+		this.payloadLength = bb.getShort();
+		this.nextHeader = IpProtocol.of(bb.get());
+		this.hopLimit = bb.get();
+		byte[] sourceAddress = new byte[16];
+		bb.get(sourceAddress, 0, 16);
+		byte[] destinationAddress = new byte[16];
+		bb.get(destinationAddress, 0, 16);
+		this.sourceAddress = IPv6Address.of(sourceAddress);
+		this.destinationAddress = IPv6Address.of(destinationAddress);
+		// Retrieve the payload, if possible.
+		IPacket payload;
+		if (IPv6.nextHeaderClassMap.containsKey(this.nextHeader)) {
+			Class<? extends IPacket> clazz = IPv6.nextHeaderClassMap.get(this.nextHeader);
+			try {
+				payload = clazz.newInstance();
+			} catch (Exception e) {
+				throw new RuntimeException("Error parsing payload for IPv6 packet", e);
+			}
+		} else {
+			payload = new Data();
+		}
+		// Deserialize as much of the payload as we can (hopefully all of it).
+		this.payload = payload.deserialize(data, bb.position(),
+				Math.min(this.payloadLength, bb.limit() - bb.position()));
+		this.payload.setParent(this);
+		// We're done!
+		return this;
+	}
+
+	/* (non-Javadoc)
+	 * @see java.lang.Object#hashCode()
+	 */
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = super.hashCode();
+		result = prime
+				* result
+				+ ((destinationAddress == null) ? 0 : destinationAddress
+						.hashCode());
+		result = prime * result + flowLabel;
+		result = prime * result + hopLimit;
+		result = prime * result
+				+ ((nextHeader == null) ? 0 : nextHeader.hashCode());
+		result = prime * result + payloadLength;
+		result = prime * result
+				+ ((sourceAddress == null) ? 0 : sourceAddress.hashCode());
+		result = prime * result + trafficClass;
+		result = prime * result + version;
+		return result;
+	}
+
+	/* (non-Javadoc)
+	 * @see java.lang.Object#equals(java.lang.Object)
+	 */
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (!super.equals(obj))
+			return false;
+		if (!(obj instanceof IPv6))
+			return false;
+		IPv6 other = (IPv6) obj;
+		if (destinationAddress == null) {
+			if (other.destinationAddress != null)
+				return false;
+		} else if (!destinationAddress.equals(other.destinationAddress))
+			return false;
+		if (flowLabel != other.flowLabel)
+			return false;
+		if (hopLimit != other.hopLimit)
+			return false;
+		if (nextHeader == null) {
+			if (other.nextHeader != null)
+				return false;
+		} else if (!nextHeader.equals(other.nextHeader))
+			return false;
+		if (payloadLength != other.payloadLength)
+			return false;
+		if (sourceAddress == null) {
+			if (other.sourceAddress != null)
+				return false;
+		} else if (!sourceAddress.equals(other.sourceAddress))
+			return false;
+		if (trafficClass != other.trafficClass)
+			return false;
+		if (version != other.version)
+			return false;
+		return true;
+	}
+}
diff --git a/src/main/java/net/floodlightcontroller/packet/SPUD.java b/src/main/java/net/floodlightcontroller/packet/SPUD.java
new file mode 100644
index 0000000000000000000000000000000000000000..d443d59dade46d471a5d75c7799c8e18cc719452
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/packet/SPUD.java
@@ -0,0 +1,153 @@
+package net.floodlightcontroller.packet;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+/**
+ * @author Jacob Chappell (jacob.chappell@uky.edu)
+ */
+public class SPUD extends BasePacket {
+    public static final byte[] MAGIC_CONSTANT =
+        { (byte) 0xd8, 0x00, 0x00, (byte) 0xd8 };
+    public static final int HEADER_LENGTH = 13;
+    public static final byte COMMAND_DATA = 0x0;
+    public static final byte COMMAND_OPEN = 0x1;
+    public static final byte COMMAND_CLOSE = 0x2;
+    public static final byte COMMAND_ACK = 0x3;
+
+    protected long tubeID;
+    protected byte command;
+    protected boolean adec;
+    protected boolean pdec;
+    protected byte reserved;
+
+    public long getTubeID() {
+        return tubeID;
+    }
+
+    public SPUD setTubeID(long tubeID) {
+        this.tubeID = tubeID;
+        return this;
+    }
+
+    public byte getCommand() {
+        return command;
+    }
+
+    public SPUD setCommand(byte command) {
+        this.command = command;
+        return this;
+    }
+
+    public boolean getADEC() {
+        return adec;
+    }
+
+    public SPUD setADEC(boolean adec) {
+        this.adec = adec;
+        return this;
+    }
+
+    public boolean getPDEC() {
+        return pdec;
+    }
+
+    public SPUD setPDEC(boolean pdec) {
+        this.pdec = pdec;
+        return this;
+    }
+
+    public byte getReserved() {
+        return reserved;
+    }
+
+    public SPUD setReserved(byte reserved) {
+        this.reserved = reserved;
+        return this;
+    }
+
+    @Override
+    public byte[] serialize() {
+        byte[] payloadData = null;
+        if (payload != null) {
+            payload.setParent(this);
+            payloadData = payload.serialize();
+        }
+        int length = HEADER_LENGTH + ((payloadData == null) ? 0 : payloadData.length);
+        byte[] data = new byte[length];
+        ByteBuffer bb = ByteBuffer.wrap(data);
+        bb.put(MAGIC_CONSTANT);
+        bb.putLong(tubeID);
+        byte adecBit = (byte) ((adec) ? 1 : 0);
+        byte pdecBit = (byte) ((pdec) ? 1 : 0);
+        byte lastByte = (byte) (((command & 0x3) << 6) | ((adecBit & 0x1) << 5)
+                | ((pdecBit & 0x1) << 4) | (reserved & 0xf));
+        bb.put(lastByte);
+        if (payloadData != null) {
+            bb.put(payloadData);
+        }
+        return data;
+    }
+
+    @Override
+    public IPacket deserialize(byte[] data, int offset, int length)
+            throws PacketParsingException {
+        ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+        byte[] magicConstant = new byte[MAGIC_CONSTANT.length];
+        bb.get(magicConstant, 0, MAGIC_CONSTANT.length);
+        if (!Arrays.equals(magicConstant, MAGIC_CONSTANT)) {
+            throw new PacketParsingException("Magic constant is incorrect.");
+        }
+        tubeID = bb.getLong();
+        byte lastByte = bb.get();
+        command = (byte) ((lastByte & 0xc0) >>> 6);
+        adec = ((lastByte & 0x20) != 0);
+        pdec = ((lastByte & 0x10) != 0);
+        reserved = (byte) (lastByte & 0xF);
+        // TODO: make sure reserved bits are 0 for this version.
+        this.payload = new Data();
+        this.payload = payload.deserialize(data, bb.position(), bb.limit()-bb.position());
+        this.payload.setParent(this);
+        return this;
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = super.hashCode();
+        result = prime * result + (adec ? 1231 : 1237);
+        result = prime * result + command;
+        result = prime * result + (pdec ? 1231 : 1237);
+        result = prime * result + reserved;
+        result = prime * result + (int) (tubeID ^ (tubeID >>> 32));
+        return result;
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (!super.equals(obj))
+            return false;
+        if (!(obj instanceof SPUD))
+            return false;
+        SPUD other = (SPUD) obj;
+        if (adec != other.adec)
+            return false;
+        if (command != other.command)
+            return false;
+        if (pdec != other.pdec)
+            return false;
+        if (reserved != other.reserved)
+            return false;
+        if (tubeID != other.tubeID)
+            return false;
+        return true;
+    }
+}
diff --git a/src/main/java/net/floodlightcontroller/packet/UDP.java b/src/main/java/net/floodlightcontroller/packet/UDP.java
index a5ee35f2c24971cca72c6d183bc2de2bea184f88..67302ae01aad3f2757a2ac9cade9856293c6e9cb 100644
--- a/src/main/java/net/floodlightcontroller/packet/UDP.java
+++ b/src/main/java/net/floodlightcontroller/packet/UDP.java
@@ -1,7 +1,7 @@
 /**
-*    Copyright 2011, Big Switch Networks, Inc. 
+*    Copyright 2011, Big Switch Networks, Inc.
 *    Originally created by David Erickson, Stanford University
-* 
+*
 *    Licensed under the Apache License, Version 2.0 (the "License"); you may
 *    not use this file except in compliance with the License. You may obtain
 *    a copy of the License at
@@ -18,6 +18,7 @@
 package net.floodlightcontroller.packet;
 
 import java.nio.ByteBuffer;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -39,7 +40,7 @@ public class UDP extends BasePacket {
          */
         UDP.decodeMap.put(DHCP_CLIENT_PORT, DHCP.class);
         UDP.decodeMap.put(DHCP_SERVER_PORT, DHCP.class);
-        
+
     }
 
     protected TransportPort sourcePort;
@@ -61,7 +62,7 @@ public class UDP extends BasePacket {
         this.sourcePort = sourcePort;
         return this;
     }
-    
+
     /**
      * @param sourcePort the sourcePort to set
      */
@@ -84,7 +85,7 @@ public class UDP extends BasePacket {
         this.destinationPort = destinationPort;
         return this;
     }
-    
+
     /**
      * @param destinationPort the destinationPort to set
      */
@@ -227,6 +228,14 @@ public class UDP extends BasePacket {
         this.destinationPort = TransportPort.of((int) (bb.getShort() & 0xffff)); // convert range 0 to 65534, not -32768 to 32767
         this.length = bb.getShort();
         this.checksum = bb.getShort();
+        // Grab a snapshot of the first four bytes of the UDP payload.
+        // We will use these to see if the payload is SPUD, without
+        // disturbing the existing byte buffer's offsets.
+        ByteBuffer bb_spud = bb.slice();
+        byte[] maybe_spud_bytes = new byte[SPUD.MAGIC_CONSTANT.length];
+        if (bb_spud.remaining() >= SPUD.MAGIC_CONSTANT.length) {
+            bb_spud.get(maybe_spud_bytes, 0, SPUD.MAGIC_CONSTANT.length);
+        }
 
         if (UDP.decodeMap.containsKey(this.destinationPort)) {
             try {
@@ -240,6 +249,9 @@ public class UDP extends BasePacket {
             } catch (Exception e) {
                 throw new RuntimeException("Failure instantiating class", e);
             }
+        } else if (Arrays.equals(maybe_spud_bytes, SPUD.MAGIC_CONSTANT)
+                && bb.remaining() >= SPUD.HEADER_LENGTH) {
+            this.payload = new SPUD();
         } else {
             this.payload = new Data();
         }
diff --git a/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java b/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java
index 015b83a01c27b76ca7f0e3c9baead2fa71faf05c..175f464b25a974709caf1c47cbffe0916d132d7e 100644
--- a/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java
+++ b/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java
@@ -21,6 +21,7 @@ import java.io.IOException;
 import java.util.EnumSet;
 import java.util.ArrayList;
 import java.util.Comparator;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
@@ -51,6 +52,7 @@ import org.projectfloodlight.openflow.protocol.OFFlowMod;
 import org.projectfloodlight.openflow.protocol.match.Match;
 import org.projectfloodlight.openflow.protocol.match.MatchField;
 import org.projectfloodlight.openflow.protocol.OFFlowModCommand;
+import org.projectfloodlight.openflow.protocol.OFFlowModFlags;
 import org.projectfloodlight.openflow.protocol.OFMessage;
 import org.projectfloodlight.openflow.protocol.OFPacketIn;
 import org.projectfloodlight.openflow.protocol.OFPacketOut;
@@ -84,6 +86,8 @@ public abstract class ForwardingBase implements IOFMessageListener {
 	public static int FLOWMOD_DEFAULT_HARD_TIMEOUT = 0; // infinite
 	public static int FLOWMOD_DEFAULT_PRIORITY = 1; // 0 is the default table-miss flow in OF1.3+, so we need to use 1
 	
+	public static boolean FLOWMOD_DEFAULT_SET_SEND_FLOW_REM_FLAG = false;
+	
 	public static boolean FLOWMOD_DEFAULT_MATCH_VLAN = true;
 	public static boolean FLOWMOD_DEFAULT_MATCH_MAC = true;
 	public static boolean FLOWMOD_DEFAULT_MATCH_IP_ADDR = true;
@@ -255,8 +259,8 @@ public abstract class ForwardingBase implements IOFMessageListener {
 			
 			OFActionOutput.Builder aob = sw.getOFFactory().actions().buildOutput();
 			List<OFAction> actions = new ArrayList<OFAction>();	
-			Match.Builder mb = MatchUtils.createRetentiveBuilder(match);
-
+ 			Match.Builder mb = MatchUtils.convertToVersion(match, sw.getOFFactory().getVersion());
+ 			
 			// set input and output ports on the switch
 			OFPort outPort = switchPortList.get(indx).getPortId();
 			OFPort inPort = switchPortList.get(indx - 1).getPortId();
@@ -265,6 +269,12 @@ public abstract class ForwardingBase implements IOFMessageListener {
 			aob.setMaxLen(Integer.MAX_VALUE);
 			actions.add(aob.build());
 			
+			if(FLOWMOD_DEFAULT_SET_SEND_FLOW_REM_FLAG) {
+				Set<OFFlowModFlags> flags = new HashSet<>();
+				flags.add(OFFlowModFlags.SEND_FLOW_REM);
+				fmb.setFlags(flags);
+			}
+			
 			// compile
 			fmb.setMatch(mb.build()) // was match w/o modifying input port
 			.setActions(actions)
diff --git a/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntries.java b/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntries.java
index 94a8686afe655dbfe2ff3ce3eb935c6fc14b24e1..36e8d8e16a0b3536c70d70cc4a6af58749dcab6f 100644
--- a/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntries.java
+++ b/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntries.java
@@ -206,116 +206,375 @@ public class StaticFlowEntries {
 			MatchField mf = itr.next();
 			switch (mf.id) {
 			case IN_PORT: // iterates over only exact/masked fields. No need to check for null entries.
-				entry.put(StaticFlowEntryPusher.COLUMN_IN_PORT, Integer.toString((match.get(MatchField.IN_PORT)).getPortNumber()));
+				if (match.supports(MatchField.IN_PORT) && match.isExact(MatchField.IN_PORT)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_IN_PORT, match.get(MatchField.IN_PORT).toString());
+				} else if (match.supportsMasked(MatchField.IN_PORT) && match.isPartiallyMasked(MatchField.IN_PORT)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_IN_PORT, match.getMasked(MatchField.IN_PORT).toString());
+				} else {
+					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
+							StaticFlowEntryPusher.COLUMN_IN_PORT, match.getVersion().toString());
+				}
 				break;
 			case ETH_SRC:
-				entry.put(StaticFlowEntryPusher.COLUMN_DL_SRC, match.get(MatchField.ETH_SRC).toString());
+				if (match.supports(MatchField.ETH_SRC) && match.isExact(MatchField.ETH_SRC)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_DL_SRC, match.get(MatchField.ETH_SRC).toString());
+				} else if (match.supportsMasked(MatchField.ETH_SRC) && match.isPartiallyMasked(MatchField.ETH_SRC)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_DL_SRC, match.getMasked(MatchField.ETH_SRC).toString());
+				} else {
+					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
+							StaticFlowEntryPusher.COLUMN_DL_SRC, match.getVersion().toString());
+				}
 				break;
 			case ETH_DST:
-				entry.put(StaticFlowEntryPusher.COLUMN_DL_DST, match.get(MatchField.ETH_DST).toString());
+				if (match.supports(MatchField.ETH_DST) && match.isExact(MatchField.ETH_DST)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_DL_DST, match.get(MatchField.ETH_DST).toString());
+				} else if (match.supportsMasked(MatchField.ETH_DST) && match.isPartiallyMasked(MatchField.ETH_DST)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_DL_DST, match.getMasked(MatchField.ETH_DST).toString());
+				} else {
+					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
+							StaticFlowEntryPusher.COLUMN_DL_DST, match.getVersion().toString());
+				}
 				break;
 			case VLAN_VID:
-				entry.put(StaticFlowEntryPusher.COLUMN_DL_VLAN, match.get(MatchField.VLAN_VID).getVlan());
+				if (match.supports(MatchField.VLAN_VID) && match.isExact(MatchField.VLAN_VID)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_DL_VLAN, match.get(MatchField.VLAN_VID).toString());
+				} else if (match.supportsMasked(MatchField.VLAN_VID) && match.isPartiallyMasked(MatchField.VLAN_VID)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_DL_VLAN, match.getMasked(MatchField.VLAN_VID).toString());
+				} else {
+					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
+							StaticFlowEntryPusher.COLUMN_DL_VLAN, match.getVersion().toString());
+				}
 				break;
 			case VLAN_PCP:
-				entry.put(StaticFlowEntryPusher.COLUMN_DL_VLAN_PCP, Byte.toString(match.get(MatchField.VLAN_PCP).getValue()));
+				if (match.supports(MatchField.VLAN_PCP) && match.isExact(MatchField.VLAN_PCP)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_DL_VLAN_PCP, match.get(MatchField.VLAN_PCP).toString());
+				} else if (match.supportsMasked(MatchField.VLAN_PCP) && match.isPartiallyMasked(MatchField.VLAN_PCP)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_DL_VLAN_PCP, match.getMasked(MatchField.VLAN_PCP).toString());
+				} else {
+					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
+							StaticFlowEntryPusher.COLUMN_DL_VLAN_PCP, match.getVersion().toString());
+				}
 				break;
 			case ETH_TYPE:
-				entry.put(StaticFlowEntryPusher.COLUMN_DL_TYPE, match.get(MatchField.ETH_TYPE).getValue());
+				if (match.supports(MatchField.ETH_TYPE) && match.isExact(MatchField.ETH_TYPE)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_DL_TYPE, match.get(MatchField.ETH_TYPE).toString());
+				} else if (match.supportsMasked(MatchField.ETH_TYPE) && match.isPartiallyMasked(MatchField.ETH_TYPE)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_DL_TYPE, match.getMasked(MatchField.ETH_TYPE).toString());
+				} else {
+					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
+							StaticFlowEntryPusher.COLUMN_DL_TYPE, match.getVersion().toString());
+				}
 				break;
 			case IP_ECN: // TOS = [DSCP bits 0-5] + [ECN bits 6-7] --> bitwise OR to get TOS byte (have separate columns now though)
-				entry.put(StaticFlowEntryPusher.COLUMN_NW_ECN, Byte.toString(match.get(MatchField.IP_ECN).getEcnValue()));
+				if (match.supports(MatchField.IP_ECN) && match.isExact(MatchField.IP_ECN)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_NW_ECN, match.get(MatchField.IP_ECN).toString());
+				} else if (match.supportsMasked(MatchField.IP_ECN) && match.isPartiallyMasked(MatchField.IP_ECN)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_NW_ECN, match.getMasked(MatchField.IP_ECN).toString());
+				} else {
+					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
+							StaticFlowEntryPusher.COLUMN_NW_ECN, match.getVersion().toString());
+				}
 				break;
 			case IP_DSCP: // Even for OF1.0, loxi will break ECN and DSCP up from the API's POV. This method is only invoked by a SFP service push from another module
-				entry.put(StaticFlowEntryPusher.COLUMN_NW_DSCP, Byte.toString((byte) (match.get(MatchField.IP_DSCP).getDscpValue())));
+				if (match.supports(MatchField.IP_DSCP) && match.isExact(MatchField.IP_DSCP)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_NW_DSCP, match.get(MatchField.IP_DSCP).toString());
+				} else if (match.supportsMasked(MatchField.IP_DSCP) && match.isPartiallyMasked(MatchField.IP_DSCP)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_NW_DSCP, match.getMasked(MatchField.IP_DSCP).toString());
+				} else {
+					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
+							StaticFlowEntryPusher.COLUMN_NW_DSCP, match.getVersion().toString());
+				}
 				break;
 			case IP_PROTO:
-				entry.put(StaticFlowEntryPusher.COLUMN_NW_PROTO, Short.toString(match.get(MatchField.IP_PROTO).getIpProtocolNumber()));
+				if (match.supports(MatchField.IP_PROTO) && match.isExact(MatchField.IP_PROTO)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_NW_PROTO, match.get(MatchField.IP_PROTO).toString());
+				} else if (match.supportsMasked(MatchField.IP_PROTO) && match.isPartiallyMasked(MatchField.IP_PROTO)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_NW_PROTO, match.getMasked(MatchField.IP_PROTO).toString());
+				} else {
+					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
+							StaticFlowEntryPusher.COLUMN_NW_PROTO, match.getVersion().toString());
+				}
 				break;
 			case IPV4_SRC:
-				entry.put(StaticFlowEntryPusher.COLUMN_NW_SRC, match.get(MatchField.IPV4_SRC).toString());
+				if (match.supports(MatchField.IPV4_SRC) && match.isExact(MatchField.IPV4_SRC)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_NW_SRC, match.get(MatchField.IPV4_SRC).toString());
+				} else if (match.supportsMasked(MatchField.IPV4_SRC) && match.isPartiallyMasked(MatchField.IPV4_SRC)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_NW_SRC, match.getMasked(MatchField.IPV4_SRC).toString());
+				} else {
+					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
+							StaticFlowEntryPusher.COLUMN_NW_SRC, match.getVersion().toString());
+				}
 				break;
 			case IPV4_DST:
-				entry.put(StaticFlowEntryPusher.COLUMN_NW_DST, match.get(MatchField.IPV4_DST).toString());
+				if (match.supports(MatchField.IPV4_DST) && match.isExact(MatchField.IPV4_DST)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_NW_DST, match.get(MatchField.IPV4_DST).toString());
+				} else if (match.supportsMasked(MatchField.IPV4_DST) && match.isPartiallyMasked(MatchField.IPV4_DST)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_NW_DST, match.getMasked(MatchField.IPV4_DST).toString());
+				} else {
+					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
+							StaticFlowEntryPusher.COLUMN_NW_DST, match.getVersion().toString());
+				}
 				break;
 			case TCP_SRC:
-				entry.put(StaticFlowEntryPusher.COLUMN_TCP_SRC, match.get(MatchField.TCP_SRC).getPort());
+				if (match.supports(MatchField.TCP_SRC) && match.isExact(MatchField.TCP_SRC)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_TCP_SRC, match.get(MatchField.TCP_SRC).toString());
+				} else if (match.supportsMasked(MatchField.TCP_SRC) && match.isPartiallyMasked(MatchField.TCP_SRC)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_TCP_SRC, match.getMasked(MatchField.TCP_SRC).toString());
+				} else {
+					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
+							StaticFlowEntryPusher.COLUMN_TCP_SRC, match.getVersion().toString());
+				}
 				break;
 			case UDP_SRC:
-				entry.put(StaticFlowEntryPusher.COLUMN_UDP_SRC, match.get(MatchField.UDP_SRC).getPort());
+				if (match.supports(MatchField.UDP_SRC) && match.isExact(MatchField.UDP_SRC)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_UDP_SRC, match.get(MatchField.UDP_SRC).toString());
+				} else if (match.supportsMasked(MatchField.UDP_SRC) && match.isPartiallyMasked(MatchField.UDP_SRC)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_UDP_SRC, match.getMasked(MatchField.UDP_SRC).toString());
+				} else {
+					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
+							StaticFlowEntryPusher.COLUMN_UDP_SRC, match.getVersion().toString());
+				}
 				break;
 			case SCTP_SRC:
-				entry.put(StaticFlowEntryPusher.COLUMN_SCTP_SRC, match.get(MatchField.SCTP_SRC).getPort());
+				if (match.supports(MatchField.SCTP_SRC) && match.isExact(MatchField.SCTP_SRC)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_SCTP_SRC, match.get(MatchField.SCTP_SRC).toString());
+				} else if (match.supportsMasked(MatchField.SCTP_SRC) && match.isPartiallyMasked(MatchField.SCTP_SRC)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_SCTP_SRC, match.getMasked(MatchField.SCTP_SRC).toString());
+				} else {
+					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
+							StaticFlowEntryPusher.COLUMN_SCTP_SRC, match.getVersion().toString());
+				}
 				break;
 			case TCP_DST:
-				entry.put(StaticFlowEntryPusher.COLUMN_TCP_DST, match.get(MatchField.TCP_DST).getPort());
+				if (match.supports(MatchField.TCP_DST) && match.isExact(MatchField.TCP_DST)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_TCP_DST, match.get(MatchField.TCP_DST).toString());
+				} else if (match.supportsMasked(MatchField.TCP_DST) && match.isPartiallyMasked(MatchField.TCP_DST)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_TCP_DST, match.getMasked(MatchField.TCP_DST).toString());
+				} else {
+					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
+							StaticFlowEntryPusher.COLUMN_TCP_DST, match.getVersion().toString());
+				}
 				break;
 			case UDP_DST:
-				entry.put(StaticFlowEntryPusher.COLUMN_UDP_DST, match.get(MatchField.UDP_DST).getPort());
+				if (match.supports(MatchField.UDP_DST) && match.isExact(MatchField.UDP_DST)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_UDP_DST, match.get(MatchField.UDP_DST).toString());
+				} else if (match.supportsMasked(MatchField.UDP_DST) && match.isPartiallyMasked(MatchField.UDP_DST)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_UDP_DST, match.getMasked(MatchField.UDP_DST).toString());
+				} else {
+					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
+							StaticFlowEntryPusher.COLUMN_UDP_DST, match.getVersion().toString());
+				}
 				break;
 			case SCTP_DST:
-				entry.put(StaticFlowEntryPusher.COLUMN_SCTP_DST, match.get(MatchField.SCTP_DST).getPort());
+				if (match.supports(MatchField.SCTP_DST) && match.isExact(MatchField.SCTP_DST)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_SCTP_DST, match.get(MatchField.SCTP_DST).toString());
+				} else if (match.supportsMasked(MatchField.SCTP_DST) && match.isPartiallyMasked(MatchField.SCTP_DST)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_SCTP_DST, match.getMasked(MatchField.SCTP_DST).toString());
+				} else {
+					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
+							StaticFlowEntryPusher.COLUMN_SCTP_DST, match.getVersion().toString());
+				}
 				break;
 			case ICMPV4_TYPE:
-				entry.put(StaticFlowEntryPusher.COLUMN_ICMP_TYPE, match.get(MatchField.ICMPV4_TYPE).getType());
+				if (match.supports(MatchField.ICMPV4_TYPE) && match.isExact(MatchField.ICMPV4_TYPE)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_ICMP_TYPE, match.get(MatchField.ICMPV4_TYPE).toString());
+				} else if (match.supportsMasked(MatchField.ICMPV4_TYPE) && match.isPartiallyMasked(MatchField.ICMPV4_TYPE)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_ICMP_TYPE, match.getMasked(MatchField.ICMPV4_TYPE).toString());
+				} else {
+					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
+							StaticFlowEntryPusher.COLUMN_ICMP_TYPE, match.getVersion().toString());
+				}
 				break;
 			case ICMPV4_CODE:
-				entry.put(StaticFlowEntryPusher.COLUMN_ICMP_CODE, match.get(MatchField.ICMPV4_CODE).getCode());
+				if (match.supports(MatchField.ICMPV4_CODE) && match.isExact(MatchField.ICMPV4_CODE)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_ICMP_CODE, match.get(MatchField.ICMPV4_CODE).toString());
+				} else if (match.supportsMasked(MatchField.ICMPV4_CODE) && match.isPartiallyMasked(MatchField.ICMPV4_CODE)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_ICMP_CODE, match.getMasked(MatchField.ICMPV4_CODE).toString());
+				} else {
+					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
+							StaticFlowEntryPusher.COLUMN_ICMP_CODE, match.getVersion().toString());
+				}
 				break;
 			case ARP_OP:
-				entry.put(StaticFlowEntryPusher.COLUMN_ARP_OPCODE, match.get(MatchField.ARP_OP).getOpcode());
+				if (match.supports(MatchField.ARP_OP) && match.isExact(MatchField.ARP_OP)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_ARP_OPCODE, match.get(MatchField.ARP_OP).toString());
+				} else if (match.supportsMasked(MatchField.ARP_OP) && match.isPartiallyMasked(MatchField.ARP_OP)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_ARP_OPCODE, match.getMasked(MatchField.ARP_OP).toString());
+				} else {
+					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
+							StaticFlowEntryPusher.COLUMN_ARP_OPCODE, match.getVersion().toString());
+				}
 				break;
 			case ARP_SHA:
-				entry.put(StaticFlowEntryPusher.COLUMN_ARP_SHA, match.get(MatchField.ARP_SHA).toString());
+				if (match.supports(MatchField.ARP_SHA) && match.isExact(MatchField.ARP_SHA)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_ARP_SHA, match.get(MatchField.ARP_SHA).toString());
+				} else if (match.supportsMasked(MatchField.ARP_SHA) && match.isPartiallyMasked(MatchField.ARP_SHA)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_ARP_SHA, match.getMasked(MatchField.ARP_SHA).toString());
+				} else {
+					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
+							StaticFlowEntryPusher.COLUMN_ARP_SHA, match.getVersion().toString());
+				}
 				break;
 			case ARP_THA:
-				entry.put(StaticFlowEntryPusher.COLUMN_ARP_DHA, match.get(MatchField.ARP_THA).toString());
+				if (match.supports(MatchField.ARP_THA) && match.isExact(MatchField.ARP_THA)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_ARP_DHA, match.get(MatchField.ARP_THA).toString());
+				} else if (match.supportsMasked(MatchField.ARP_THA) && match.isPartiallyMasked(MatchField.ARP_THA)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_ARP_DHA, match.getMasked(MatchField.ARP_THA).toString());
+				} else {
+					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
+							StaticFlowEntryPusher.COLUMN_ARP_DHA, match.getVersion().toString());
+				}
 				break;
 			case ARP_SPA:
-				entry.put(StaticFlowEntryPusher.COLUMN_ARP_SPA, match.get(MatchField.ARP_SPA).toString());
+				if (match.supports(MatchField.ARP_SPA) && match.isExact(MatchField.ARP_SPA)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_ARP_SPA, match.get(MatchField.ARP_SPA).toString());
+				} else if (match.supportsMasked(MatchField.ARP_SPA) && match.isPartiallyMasked(MatchField.ARP_SPA)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_ARP_SPA, match.getMasked(MatchField.ARP_SPA).toString());
+				} else {
+					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
+							StaticFlowEntryPusher.COLUMN_ARP_SPA, match.getVersion().toString());
+				}
 				break;
 			case ARP_TPA:
-				entry.put(StaticFlowEntryPusher.COLUMN_ARP_DPA, match.get(MatchField.ARP_TPA).toString());
+				if (match.supports(MatchField.ARP_TPA) && match.isExact(MatchField.ARP_TPA)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_ARP_DPA, match.get(MatchField.ARP_TPA).toString());
+				} else if (match.supportsMasked(MatchField.ARP_TPA) && match.isPartiallyMasked(MatchField.ARP_TPA)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_ARP_DPA, match.getMasked(MatchField.ARP_TPA).toString());
+				} else {
+					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
+							StaticFlowEntryPusher.COLUMN_ARP_DPA, match.getVersion().toString());
+				}
 				break;
 			case IPV6_SRC:				
-				entry.put(StaticFlowEntryPusher.COLUMN_NW6_SRC, match.get(MatchField.IPV6_SRC).toString());
+				if (match.supports(MatchField.IPV6_SRC) && match.isExact(MatchField.IPV6_SRC)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_NW6_SRC, match.get(MatchField.IPV6_SRC).toString());
+				} else if (match.supportsMasked(MatchField.IPV6_SRC) && match.isPartiallyMasked(MatchField.IPV6_SRC)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_NW6_SRC, match.getMasked(MatchField.IPV6_SRC).toString());
+				} else {
+					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
+							StaticFlowEntryPusher.COLUMN_NW6_SRC, match.getVersion().toString());
+				}
 				break;
 			case IPV6_DST:			
-				entry.put(StaticFlowEntryPusher.COLUMN_NW6_DST, match.get(MatchField.IPV6_DST).toString());
+				if (match.supports(MatchField.IPV6_DST) && match.isExact(MatchField.IPV6_DST)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_NW6_DST, match.get(MatchField.IPV6_DST).toString());
+				} else if (match.supportsMasked(MatchField.IPV6_DST) && match.isPartiallyMasked(MatchField.IPV6_DST)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_NW6_DST, match.getMasked(MatchField.IPV6_DST).toString());
+				} else {
+					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
+							StaticFlowEntryPusher.COLUMN_NW6_DST, match.getVersion().toString());
+				}
 				break;	
 			case IPV6_FLABEL:			
-				entry.put(StaticFlowEntryPusher.COLUMN_IPV6_FLOW_LABEL, match.get(MatchField.IPV6_FLABEL).toString());
+				if (match.supports(MatchField.IPV6_FLABEL) && match.isExact(MatchField.IPV6_FLABEL)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_IPV6_FLOW_LABEL, match.get(MatchField.IPV6_FLABEL).toString());
+				} else if (match.supportsMasked(MatchField.IPV6_FLABEL) && match.isPartiallyMasked(MatchField.IPV6_FLABEL)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_IPV6_FLOW_LABEL, match.getMasked(MatchField.IPV6_FLABEL).toString());
+				} else {
+					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
+							StaticFlowEntryPusher.COLUMN_IPV6_FLOW_LABEL, match.getVersion().toString());
+				}
 				break;	
 			case ICMPV6_TYPE:				
-				entry.put(StaticFlowEntryPusher.COLUMN_ICMP6_TYPE, String.valueOf(match.get(MatchField.ICMPV6_TYPE).getValue()));
+				if (match.supports(MatchField.ICMPV6_TYPE) && match.isExact(MatchField.ICMPV6_TYPE)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_ICMP6_TYPE, match.get(MatchField.ICMPV6_TYPE).toString());
+				} else if (match.supportsMasked(MatchField.ICMPV6_TYPE) && match.isPartiallyMasked(MatchField.ICMPV6_TYPE)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_ICMP6_TYPE, match.getMasked(MatchField.ICMPV6_TYPE).toString());
+				} else {
+					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
+							StaticFlowEntryPusher.COLUMN_ICMP6_TYPE, match.getVersion().toString());
+				}
 				break;
 			case ICMPV6_CODE:				
-				entry.put(StaticFlowEntryPusher.COLUMN_ICMP6_CODE, match.get(MatchField.ICMPV6_CODE).getValue());
+				if (match.supports(MatchField.ICMPV6_CODE) && match.isExact(MatchField.ICMPV6_CODE)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_ICMP6_CODE, match.get(MatchField.ICMPV6_CODE).toString());
+				} else if (match.supportsMasked(MatchField.ICMPV6_CODE) && match.isPartiallyMasked(MatchField.ICMPV6_CODE)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_ICMP6_CODE, match.getMasked(MatchField.ICMPV6_CODE).toString());
+				} else {
+					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
+							StaticFlowEntryPusher.COLUMN_ICMP6_CODE, match.getVersion().toString());
+				}
 				break;
 			case IPV6_ND_SLL:			
-				entry.put(StaticFlowEntryPusher.COLUMN_ND_SLL, match.get(MatchField.IPV6_ND_SLL).toString());
-				break;
+				if (match.supports(MatchField.IPV6_ND_SLL) && match.isExact(MatchField.IPV6_ND_SLL)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_ND_SLL, match.get(MatchField.IPV6_ND_SLL).toString());
+				} else if (match.supportsMasked(MatchField.IPV6_ND_SLL) && match.isPartiallyMasked(MatchField.IPV6_ND_SLL)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_ND_SLL, match.getMasked(MatchField.IPV6_ND_SLL).toString());
+				} else {
+					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
+							StaticFlowEntryPusher.COLUMN_ND_SLL, match.getVersion().toString());
+				}
+				break;	
 			case IPV6_ND_TLL:				
-				entry.put(StaticFlowEntryPusher.COLUMN_ND_TLL, match.get(MatchField.IPV6_ND_TLL).toString());
+				if (match.supports(MatchField.IPV6_ND_TLL) && match.isExact(MatchField.IPV6_ND_TLL)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_ND_TLL, match.get(MatchField.IPV6_ND_TLL).toString());
+				} else if (match.supportsMasked(MatchField.IPV6_ND_TLL) && match.isPartiallyMasked(MatchField.IPV6_ND_TLL)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_ND_TLL, match.getMasked(MatchField.IPV6_ND_TLL).toString());
+				} else {
+					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
+							StaticFlowEntryPusher.COLUMN_ND_TLL, match.getVersion().toString());
+				}
 				break;	
 			case IPV6_ND_TARGET:				
-				entry.put(StaticFlowEntryPusher.COLUMN_ND_TARGET, match.get(MatchField.IPV6_ND_TARGET).toString());
+				if (match.supports(MatchField.IPV6_ND_TARGET) && match.isExact(MatchField.IPV6_ND_TARGET)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_ND_TARGET, match.get(MatchField.IPV6_ND_TARGET).toString());
+				} else if (match.supportsMasked(MatchField.IPV6_ND_TARGET) && match.isPartiallyMasked(MatchField.IPV6_ND_TARGET)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_ND_TARGET, match.getMasked(MatchField.IPV6_ND_TARGET).toString());
+				} else {
+					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
+							StaticFlowEntryPusher.COLUMN_ND_TARGET, match.getVersion().toString());
+				}
 				break;					
 			case MPLS_LABEL:
-				entry.put(StaticFlowEntryPusher.COLUMN_MPLS_LABEL, match.get(MatchField.MPLS_LABEL).getValue());
+				if (match.supports(MatchField.MPLS_LABEL) && match.isExact(MatchField.MPLS_LABEL)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_MPLS_LABEL, match.get(MatchField.MPLS_LABEL).toString());
+				} else if (match.supportsMasked(MatchField.MPLS_LABEL) && match.isPartiallyMasked(MatchField.MPLS_LABEL)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_MPLS_LABEL, match.getMasked(MatchField.MPLS_LABEL).toString());
+				} else {
+					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
+							StaticFlowEntryPusher.COLUMN_MPLS_LABEL, match.getVersion().toString());
+				}
 				break;
 			case MPLS_TC:
-				entry.put(StaticFlowEntryPusher.COLUMN_MPLS_TC, match.get(MatchField.MPLS_TC).getValue());
+				if (match.supports(MatchField.MPLS_TC) && match.isExact(MatchField.MPLS_TC)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_MPLS_TC, match.get(MatchField.MPLS_TC).toString());
+				} else if (match.supportsMasked(MatchField.MPLS_TC) && match.isPartiallyMasked(MatchField.MPLS_TC)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_MPLS_TC, match.getMasked(MatchField.MPLS_TC).toString());
+				} else {
+					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
+							StaticFlowEntryPusher.COLUMN_MPLS_TC, match.getVersion().toString());
+				}
 				break;
 			case MPLS_BOS:
-				entry.put(StaticFlowEntryPusher.COLUMN_MPLS_BOS, match.get(MatchField.MPLS_BOS).getValue());
+				if (match.supports(MatchField.MPLS_BOS) && match.isExact(MatchField.MPLS_BOS)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_MPLS_BOS, match.get(MatchField.MPLS_BOS).toString());
+				} else if (match.supportsMasked(MatchField.MPLS_BOS) && match.isPartiallyMasked(MatchField.MPLS_BOS)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_MPLS_BOS, match.getMasked(MatchField.MPLS_BOS).toString());
+				} else {
+					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
+							StaticFlowEntryPusher.COLUMN_MPLS_BOS, match.getVersion().toString());
+				}
 				break;			
 			case METADATA:
-				entry.put(StaticFlowEntryPusher.COLUMN_METADATA, match.get(MatchField.METADATA).getValue().getValue());
+				if (match.supports(MatchField.METADATA) && match.isExact(MatchField.METADATA)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_METADATA, match.get(MatchField.METADATA).toString());
+				} else if (match.supportsMasked(MatchField.METADATA) && match.isPartiallyMasked(MatchField.METADATA)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_METADATA, match.getMasked(MatchField.METADATA).toString());
+				} else {
+					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
+							StaticFlowEntryPusher.COLUMN_METADATA, match.getVersion().toString());
+				}
 				break;
 			case TUNNEL_ID:
-				entry.put(StaticFlowEntryPusher.COLUMN_TUNNEL_ID, match.get(MatchField.TUNNEL_ID).getValue());
-				break;				
+				if (match.supports(MatchField.TUNNEL_ID) && match.isExact(MatchField.TUNNEL_ID)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_TUNNEL_ID, match.get(MatchField.TUNNEL_ID).toString());
+				} else if (match.supportsMasked(MatchField.TUNNEL_ID) && match.isPartiallyMasked(MatchField.TUNNEL_ID)) {
+					entry.put(StaticFlowEntryPusher.COLUMN_TUNNEL_ID, match.getMasked(MatchField.TUNNEL_ID).toString());
+				} else {
+					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
+							StaticFlowEntryPusher.COLUMN_TUNNEL_ID, match.getVersion().toString());
+				}
+				break;	
 			// case PBB_ISID not implemented in loxi
 			default:
 				log.error("Unhandled Match when parsing OFFlowMod: {}, {}", mf, mf.id);
diff --git a/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java b/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java
index 89f351627bc8993bd321f66b332263410a46a41d..e0f33f14be22fa12358503e06007abd561068f85 100644
--- a/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java
+++ b/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java
@@ -362,7 +362,14 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService,
 			}
 
 			// get the correct builder for the OF version supported by the switch
-			fmb = OFFactories.getFactory(switchService.getSwitch(DatapathId.of(switchName)).getOFFactory().getVersion()).buildFlowModify();
+			try {
+				fmb = OFFactories.getFactory(switchService.getSwitch(DatapathId.of(switchName)).getOFFactory().getVersion()).buildFlowModify();
+			} catch (NullPointerException e) {
+				/* switch was not connected/known */
+				storageSourceService.deleteRowAsync(TABLE_NAME, entryName);
+				log.error("Deleting entry {}. Switch {} was not connected to the controller, and we need to know the OF protocol version to compose the flow mod.", entryName, switchName);
+				return;
+			}
 
 			StaticFlowEntries.initDefaultFlowMod(fmb, entryName);
 
@@ -587,16 +594,20 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService,
 		}
 
 		// send flow_mod delete
-		OFFlowDeleteStrict flowMod = FlowModUtils.toFlowDeleteStrict(entriesFromStorage.get(dpid).get(entryName));
+		if (switchService.getSwitch(DatapathId.of(dpid)) != null) {
+			OFFlowDeleteStrict flowMod = FlowModUtils.toFlowDeleteStrict(entriesFromStorage.get(dpid).get(entryName));
+
+			if (entriesFromStorage.containsKey(dpid) && entriesFromStorage.get(dpid).containsKey(entryName)) {
+				entriesFromStorage.get(dpid).remove(entryName);
+			} else {
+				log.debug("Tried to delete non-existent entry {} for switch {}", entryName, dpid);
+				return;
+			}
 
-		if (entriesFromStorage.containsKey(dpid) && entriesFromStorage.get(dpid).containsKey(entryName)) {
-			entriesFromStorage.get(dpid).remove(entryName);
+			writeFlowModToSwitch(DatapathId.of(dpid), flowMod);
 		} else {
-			log.debug("Tried to delete non-existent entry {} for switch {}", entryName, dpid);
-			return;
+			log.debug("Not sending flow delete for disconnected switch.");
 		}
-
-		writeFlowModToSwitch(DatapathId.of(dpid), flowMod);
 		return;
 	}
 
@@ -730,19 +741,19 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService,
 					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.getVersion().compareTo(OFVersion.OF_12) < 0 ? true : 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())
-							) {
+								(msg.getVersion().compareTo(OFVersion.OF_10) == 0 ? true : 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
diff --git a/src/main/java/net/floodlightcontroller/testmodule/TestModule.java b/src/main/java/net/floodlightcontroller/testmodule/TestModule.java
index 85fbf0f46fa1fee79dda7f06e2ca590fb7c519cb..4c16876ec66614833d154b9aa351666c8cfb07b4 100644
--- a/src/main/java/net/floodlightcontroller/testmodule/TestModule.java
+++ b/src/main/java/net/floodlightcontroller/testmodule/TestModule.java
@@ -6,6 +6,7 @@ import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.ExecutionException;
 
 import org.projectfloodlight.openflow.protocol.OFFactories;
 import org.projectfloodlight.openflow.protocol.OFFactory;
@@ -16,6 +17,8 @@ import org.projectfloodlight.openflow.protocol.OFMeterBandType;
 import org.projectfloodlight.openflow.protocol.OFMeterConfig;
 import org.projectfloodlight.openflow.protocol.OFMeterMod;
 import org.projectfloodlight.openflow.protocol.OFMeterModCommand;
+import org.projectfloodlight.openflow.protocol.OFMeterStats;
+import org.projectfloodlight.openflow.protocol.OFMeterStatsReply;
 import org.projectfloodlight.openflow.protocol.OFMeterStatsRequest;
 import org.projectfloodlight.openflow.protocol.OFOxmClass;
 import org.projectfloodlight.openflow.protocol.OFPortDesc;
@@ -76,6 +79,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.util.concurrent.ListenableFuture;
 
 import net.floodlightcontroller.core.IOFSwitchListener;
 import net.floodlightcontroller.core.PortChangeType;
@@ -141,6 +145,20 @@ public class TestModule implements IFloodlightModule, IOFSwitchListener {
 		if (switchId.equals(DatapathId.of(1)))
 		switchService.getSwitch(switchId).write(fab.build());*/
 		
+		/*OFMeterStatsRequest req;
+		ListenableFuture<OFMeterStatsReply> reply = switchService.getActiveSwitch(switchId).writeRequest(req);
+		try {
+			for (OFMeterStats entry : reply.get().getEntries()) {
+				U64 byteInCount = entry.getByteInCount();
+				for (OFMeterBandStats mbs : entry.getBandStats()) {
+					U64 byteBandCount = mbs.getByteBandCount();
+					U64 pktBandCount = mbs.getPacketBandCount();
+				}
+			}
+		} catch (InterruptedException | ExecutionException e) {
+			e.printStackTrace();
+		}*/
+		
 		OFFactory f =factory;
         Match.Builder mb =f.buildMatch();
         mb.setExact(MatchField.ETH_SRC, MacAddress.of(2));
diff --git a/src/main/java/net/floodlightcontroller/topology/TopologyInstance.java b/src/main/java/net/floodlightcontroller/topology/TopologyInstance.java
index cf2183a2e8d1f016a68496a4e9cffc38e5dec064..7568dad7d11139915ac8b480eca1cb837d677005 100644
--- a/src/main/java/net/floodlightcontroller/topology/TopologyInstance.java
+++ b/src/main/java/net/floodlightcontroller/topology/TopologyInstance.java
@@ -91,7 +91,7 @@ public class TopologyInstance {
 
         @Override
         public Route load(RouteId rid) {
-            return ti.buildroute(rid);
+        	return ti.buildroute(rid);
         }
     }
 
@@ -638,7 +638,7 @@ public class TopologyInstance {
         if (log.isTraceEnabled()) {
             log.trace("buildroute: {}", result);
         }
-        return result;
+        return result; 
     }
 
     protected int getCost(DatapathId srcId, DatapathId dstId) {
diff --git a/src/main/java/net/floodlightcontroller/util/MatchUtils.java b/src/main/java/net/floodlightcontroller/util/MatchUtils.java
index ea3d77cf26075acbd1c602052c66e25c04b0156c..c458a9f2e852e5ac79827828de0a8ce2b3bf4054 100644
--- a/src/main/java/net/floodlightcontroller/util/MatchUtils.java
+++ b/src/main/java/net/floodlightcontroller/util/MatchUtils.java
@@ -11,6 +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.IPv6AddressWithMask;
 import org.projectfloodlight.openflow.types.IPv6FlowLabel;
@@ -104,6 +105,8 @@ public class MatchUtils {
 
 	public static final String STR_METADATA = "metadata";
 	public static final String STR_TUNNEL_ID = "tunnel_id";
+	public static final String STR_TUNNEL_IPV4_SRC = "tunnel_ipv4_src";
+	public static final String STR_TUNNEL_IPV4_DST = "tunnel_ipv4_dst";
 
 	public static final String STR_PBB_ISID = "pbb_isid";	
 
@@ -152,27 +155,27 @@ public class MatchUtils {
 		}
 		return mb.build();
 	}
-
+	
+	
 	/**
+	 * Retains all fields in the Match parent. Converts the parent to an
+	 * equivalent Match of OFVersion version. No polite check is done to verify 
+	 * if MatchFields in parent are supported in a Match of OFVersion
+	 * version. An exception will be thrown if there are any unsupported
+	 * fields during the conversion process.
 	 * 
-	 * Workaround for bug in Loxi:
-	 * 
-	 * 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.
+	 * Note that a Match.Builder is returned. This is a convenience for cases
+	 * where MatchFields might be modified, added, or removed prior to being
+	 * built (e.g. in Forwarding/Routing between switches of different OFVersions).
+	 * Simply build the returned Match.Builder if you would like to treat this
+	 * function as a strict copy-to-version.
 	 * 
-	 * @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.
+	 * @param parent, the Match to convert
+	 * @param version, the OFVersion to convert parent to
+	 * @return a Match.Builder of the newly-converted Match
 	 */
-	@SuppressWarnings({ "unchecked", "rawtypes" })
-	public static Match.Builder createRetentiveBuilder(Match m) {
+	@SuppressWarnings("unchecked")
+	public static Match.Builder convertToVersion(Match parent, OFVersion version) {
 		/* 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,  
@@ -182,14 +185,15 @@ public class MatchUtils {
 		 * 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)
+		Match.Builder mb = OFFactories.getFactory(version).buildMatch(); 
+		Iterator<MatchField<?>> itr = parent.getMatchFields().iterator(); // only get exact or masked fields (not fully wildcarded)
 		while(itr.hasNext()) {
+			@SuppressWarnings("rawtypes")
 			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));
+			if (parent.isExact(mf)) {
+				mb.setExact(mf, parent.get(mf));
+			} else if (parent.isPartiallyMasked(mf)) {
+				mb.setMasked(mf, parent.getMasked(mf));
 			} else {
 				// it's either exact, masked, or wildcarded
 				// itr only contains exact and masked MatchFields
@@ -199,6 +203,28 @@ public class MatchUtils {
 		return mb;
 	}
 
+	/**
+	 * 
+	 * Workaround for bug in Loxi:
+	 * 
+	 * 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.
+	 */
+	public static Match.Builder createRetentiveBuilder(Match m) {
+		return convertToVersion(m, m.getVersion());
+	}
+
 	/**
 	 * 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
@@ -748,6 +774,20 @@ public class MatchUtils {
 							dataMask[1].contains("0x") ? U64.of(Long.valueOf(dataMask[1].replaceFirst("0x", ""), 16)) : U64.of(Long.valueOf(dataMask[1])));
 				}
 				break;
+			case STR_TUNNEL_IPV4_SRC:
+				if (dataMask.length == 1) {
+					mb.setExact(MatchField.TUNNEL_IPV4_SRC, IPv4Address.of(key_value[1]));
+				} else {
+					mb.setMasked(MatchField.TUNNEL_IPV4_SRC, IPv4AddressWithMask.of(key_value[1]));
+				}
+				break;
+			case STR_TUNNEL_IPV4_DST:
+				if (dataMask.length == 1) {
+					mb.setExact(MatchField.TUNNEL_IPV4_DST, IPv4Address.of(key_value[1]));
+				} else {
+					mb.setMasked(MatchField.TUNNEL_IPV4_DST, IPv4AddressWithMask.of(key_value[1]));
+				}
+				break;
 			case STR_PBB_ISID:
 				/*TODO no-op. Not implemented.
 				if (key_value[1].startsWith("0x")) {
diff --git a/src/main/java/net/floodlightcontroller/util/OFDPAUtils.java b/src/main/java/net/floodlightcontroller/util/OFDPAUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..f2744985b46813c045828174c8142dab3f418e54
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/util/OFDPAUtils.java
@@ -0,0 +1,784 @@
+package net.floodlightcontroller.util;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+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.OFBufferId;
+import org.projectfloodlight.openflow.types.OFGroup;
+import org.projectfloodlight.openflow.types.OFPort;
+import org.projectfloodlight.openflow.types.OFVlanVidMatch;
+import org.projectfloodlight.openflow.types.TableId;
+import org.projectfloodlight.openflow.types.U16;
+import org.projectfloodlight.openflow.types.U32;
+import org.projectfloodlight.openflow.types.U64;
+import org.projectfloodlight.openflow.types.VlanVid;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The Broadcom OF-DPA 2.0 specification provides an interesting
+ * packet processing pipeline. Most OpenFlow programmers are
+ * accustomed to programming Open vSwitch or other software switches,
+ * where much if not all of the OpenFlow specification is supported.
+ * 
+ * Hardware switch vendors wishing to support OpenFlow typically mimic
+ * software switches and allow programmers access to uniform flow
+ * tables, where the switch will automatically move operations not 
+ * supported in hardware to software/the CPU, also known as the
+ * "slow path." In order to operate on packets in hardware, switch
+ * vendors need to expose some of the low-level details, features, and
+ * shortcomings to the programmer. That is where OF-DPA comes in.
+ * 
+ * On compatible Broadcom-based switches, in conjunction with the Indigo
+ * OpenFlow agent running onboard the switch, OpenFlow controllers can
+ * leverage complete hardware forwarding of packets in the data plane.
+ * This does not necessarily include the ability to perform all actions
+ * and matches in hardware; however, it increases the available set of
+ * actions and matches and give the controller access to powerful
+ * features onboard the switch, such as routing and tunneling.
+ * 
+ * OFDPAUtils provides an abstraction when programming a Broadcom
+ * OF-DPA switch in order to ease conformance to the OF-DPA 2.0 spec
+ * from the controller and modules.
+ * 
+ * @author Ryan Izard, ryan.izard@bigswitch.com, rizard@g.clemson.edu
+ */
+public class OFDPAUtils {
+
+	private OFDPAUtils() {}
+
+	private static final Logger log = LoggerFactory.getLogger(OFDPAUtils.class);
+
+	/* 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 */
+		static final int L2_MULTICAST = 3; 			/* 3 */
+		static final int L2_FLOOD = 4; 				/* 4 */
+		static final int L3_INTERFACE = 5; 			/* 5 */
+		static final int L3_MULTICAST = 6;			/* 6 */
+		static final int L3_ECMP = 7;				/* 7 */
+		static final int L2_DATA_CENTER_OVERLAY = 8; /* 8 */
+		static final int MPLS_LABEL = 9; 			/* 9 */
+		static final int MPLS_FORWARDING = 10;		/* 10 */
+		static final int L2_UNFILTERED_INTERFACE = 11; /* 11 */
+		static final int L2_LOOPBACK = 12;   		/* 12 */
+	}
+
+	private static class L2OverlaySubType {
+		static final int L2_OVERLAY_FLOOD_OVER_UNICAST_TUNNELS = 0;
+		static final int L2_OVERLAY_FLOOD_OVER_MULTICAST_TUNNELS = 1;
+		static final int L2_OVERLAY_MULTICAST_OVER_UNICAST_TUNNELS = 2;
+		static final int L2_OVERLAY_MULTICAST_OVER_MULTICAST_TUNNELS = 3;
+	}
+
+	private static class MPLSSubType {
+		static final int MPLS_INTERFACE = 0;
+		static final int MPLS_L2_VPN_LABEL = 1;
+		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_FAST_FAILOVER = 6;
+		static final int MPLS_ECMP = 8;
+		static final int MPLS_L2_TAG = 10;
+	}
+
+	public static class Tables {
+		public static final TableId INGRESS_PORT = TableId.of(0);
+		public static final TableId VLAN = TableId.of(10);
+		public static final TableId TERMINATION_MAC = TableId.of(20);
+		public static final TableId UNICAST_ROUTING = TableId.of(30);
+		public static final TableId MULITCAST_ROUTING = TableId.of(40);
+		public static final TableId BRIDGING = TableId.of(50);
+		public static final TableId POLICY_ACL = TableId.of(60);
+	}
+
+	private static final List<MatchFields> ALLOWED_MATCHES = 
+			Collections.unmodifiableList(
+					Arrays.asList(
+							MatchFields.IN_PORT,
+							MatchFields.ETH_SRC, 
+							MatchFields.ETH_DST,
+							MatchFields.ETH_TYPE,
+							MatchFields.VLAN_VID,
+							MatchFields.VLAN_PCP,
+							MatchFields.TUNNEL_ID,			
+							MatchFields.IP_PROTO,
+							MatchFields.IPV4_SRC,
+							MatchFields.IPV4_DST,
+							MatchFields.IP_DSCP,
+							MatchFields.IP_ECN,			
+							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
+							)
+					);
+
+	/**
+	 * Determine whether or not the provided switch is an OF-DPA switch.
+	 * 
+	 * @param sw, the switch to check
+	 * @return true if the switch is an OF-DPA switch; false otherwise
+	 */
+	public static boolean isOFDPASwitch(IOFSwitch s) {
+		/*
+		 * Many switches might have the same table IDs as an OF-DPA switch,
+		 * so we can't check the IDs only. Alternatively, we can check the
+		 * name, description, etc. to see if it aligns with an OF-DPA-set
+		 * description. This isn't fool-proof, but it'll work for now.
+		 */
+		if (s.getSwitchDescription().getSoftwareDescription().toLowerCase().contains("of-dpa")) {
+			return true;
+		} else {
+			return false;
+		}
+	}
+
+	/**
+	 * Get the MatchFields that an OF-DPA switch supports matching.
+	 * Note that this does not specify match codependencies or
+	 * mutually exclusive matches.
+	 * 
+	 * @return, an unmodifiable list of potential MatchFields
+	 */
+	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.
+	 * 
+	 * @param m
+	 * @return
+	 */
+	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)) {
+				if (unsupported == null) {
+					unsupported = new ArrayList<MatchFields>();
+				}
+				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)
+		 * 
+		 * Groups must be added last, since they require the VLANs to be added first.
+		 */
+		return addLearningSwitchPrereqFlows(sw, vlan, ports) && addLearningSwitchPrereqGroups(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());
+
+			/* We have to do this for OF-DPA. It's like adding the VLAN to the switch on that port. */
+			instructions.add(sw.getOFFactory().instructions().applyActions(Collections.singletonList((OFAction) 
+					sw.getOFFactory().actions().setField(sw.getOFFactory().oxms().vlanVid(OFVlanVidMatch.ofVlanVid((vlan.equals(VlanVid.ZERO) ? VlanVid.ofVlan(1) : vlan))))
+					)));
+			/* 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/auto-forwarding 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
+	 * MAC address. If you would like to match on an untagged ethernet
+	 * frame, omit MatchField.VLAN_VID from the Match object so that it
+	 * is wildcarded. To the contrary, if you would like to match on a 
+	 * specific VLAN tag, please include this field. It is not possible
+	 * to match on both tagged and untagged packets in a single flow.
+	 * 
+	 * Implementation note: If a VLAN tag is not provided in the Match, the
+	 * default VLAN of 1 will be assigned for internal use only; it will
+	 * not appear on any packets egress the switch.
+	 * 
+	 * If the packet is to be sent out on a different VLAN (e.g. for VLAN
+	 * translation) then the VlanVid must be specified.
+	 * 
+	 * An output OFPort must be specified. If the desired action is to 
+	 * flood, use OFPort.FLOOD; for sending to the controller, use
+	 * OFPort.CONTROLLER; etc.
+	 * 
+	 * @param s, must be an OF-DPA switch
+	 * @param m, must contain at least the destination MAC
+	 * @param outVlan, either a valid VLAN ID or ZERO for untagged
+	 * @param outPort, either a valid physical port number or ZERO (for drop), ALL, FLOOD, or CONTROLLER
+	 * @return true upon success; false if switch is not an OF-DPA switch
+	 */
+	public static boolean addBridgingFlow(IOFSwitch sw, U64 cookie, int priority, int hardTimeout, int idleTimeout, Match match, VlanVid outVlan, OFPort outPort) {
+		if (!isOFDPASwitch(sw)) {
+			log.error("Switch {} is not an OF-DPA switch. Not inserting flows.", sw.getId().toString());
+			return false;
+		}
+
+		/*
+		 * Prepare and/or check arguments against requirements.
+		 */
+		cookie = (cookie == null ? U64.ZERO : cookie);
+		priority = (priority < 1 ? 1 : priority);
+		hardTimeout = (hardTimeout < 0 ? 0 : hardTimeout);
+		idleTimeout = (idleTimeout < 0 ? 0 : idleTimeout);
+		if (match == null || !match.isExact(MatchField.ETH_DST)) {
+			log.error("OF-DPA 2.0 requires at least the destination MAC be matched in order to forward through its pipeline.");
+			return false;
+		} else {
+			List<MatchFields> mfs = checkMatchFields(match);
+			if (mfs != null) {
+				log.error("OF-DPA 2.0 does not support matching on the following fields: {}", mfs.toString());
+				return false;
+			}
+		}
+		outVlan = (outVlan == null ? VlanVid.ZERO : outVlan);
+		outPort = (outPort == null ? OFPort.ZERO : outPort);
+
+		/*
+		 * Ingress flow table (0) will automatically send to the
+		 * VLAN flow table (10), so insert nothing here.
+		 */
+
+		/*
+		 * VLAN flow table (10) is handled by prereq flows.
+		 */
+
+		/*
+		 * 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). 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.
+		 * 
+		 * Can send to controller.
+		 */
+
+		/*
+		 * Policy ACL table (60) allows for more detailed matches. This
+		 * is where we will implement all the matches specified in the Match
+		 * object. The write-actions goto group inserted by the bridging 
+		 * table (50), or here (60), will be the output action taken upon 
+		 * a match, since this is the end of the pipeline. If we want to 
+		 * drop a packet for not matching, then no output group will be
+		 * assigned to the packet, thus dropping it.
+		 * 
+		 * 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. */
+		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, (outVlan.equals(VlanVid.ZERO) ? VlanVid.ofVlan(1) : outVlan))
+							)
+					);
+			instructions.add(sw.getOFFactory().instructions().writeActions(actions));
+		} else if (outPort.equals(OFPort.CONTROLLER)) {
+			actions.add(sw.getOFFactory().actions().output(OFPort.CONTROLLER, 0xFFffFFff));
+			instructions.add(sw.getOFFactory().instructions().applyActions(actions));
+		} else { /* assume port is a number valid on the switch */
+			actions.add(
+					sw.getOFFactory().actions().group(
+							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 (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)
+				.build();
+		sw.write(fa);
+
+		return true;
+	}
+
+	/**
+	 * Generates group IDs according to the OF-DPA 2.0 spec. A create
+	 * helper function is provided for each type of group, including
+	 * sub-types, which should be used to set a valid group ID in any
+	 * group mod sent to any OF-DPA switch. 
+	 */
+	public static class GroupIds {
+		private GroupIds() {}
+
+		public static OFGroup createL2Interface(OFPort p, VlanVid v) { //0
+			return OFGroup.of(0 | p.getShortPortNumber() | (v.getVlan() << 16) | (OFDPAGroupType.L2_INTERFACE << 28));
+		}
+		/**
+		 * Only bits 0-27 of id are used. Bits 28-31 are ignored.
+		 * @param id
+		 * @return
+		 */
+		public static OFGroup createL2Rewrite(U32 id) { //1
+			return OFGroup.of(0 | (id.getRaw() & 0x0FffFFff) | (OFDPAGroupType.L2_REWRITE << 28));
+		}
+		/**
+		 * Only bits 0-27 of id are used. Bits 28-31 are ignored.
+		 * @param id
+		 * @return
+		 */
+		public static OFGroup createL3Unicast(U32 id) { //2
+			return OFGroup.of(0 | (id.getRaw() & 0x0FffFFff) | (OFDPAGroupType.L3_UNICAST << 28));
+		}
+		public static OFGroup createL2Multicast(U16 id, VlanVid v) { //3
+			return OFGroup.of(0 | id.getRaw() | (v.getVlan() << 16) | (OFDPAGroupType.L2_MULTICAST << 28));
+		}
+		public static OFGroup createL2Flood(U16 id, VlanVid v) { //4
+			return OFGroup.of(0 | id.getRaw() | (v.getVlan() << 16) | (OFDPAGroupType.L2_FLOOD << 28));
+		}
+		/**
+		 * Only bits 0-27 of id are used. Bits 28-31 are ignored.
+		 * @param id
+		 * @return
+		 */
+		public static OFGroup createL3Interface(U32 id) { //5
+			return OFGroup.of(0 | (id.getRaw() & 0x0FffFFff) | (OFDPAGroupType.L3_INTERFACE << 28));
+		}
+		public static OFGroup createL3Multicast(U16 id, VlanVid v) { //6
+			return OFGroup.of(0 | id.getRaw() | (v.getVlan() << 16) | (OFDPAGroupType.L3_MULTICAST << 28));
+		}
+		/**
+		 * Only bits 0-27 of id are used. Bits 28-31 are ignored.
+		 * @param id
+		 * @return
+		 */
+		public static OFGroup createL3ECMP(U32 id) { //7
+			return OFGroup.of(0 | (id.getRaw() & 0x0FffFFff) | (OFDPAGroupType.L3_ECMP << 28));
+		}
+		/**
+		 * Only bits 0-9 of index are used. Bits 10-15 are ignored.
+		 * @param index
+		 * @param tunnelId
+		 * @return
+		 */
+		public static OFGroup createL2DCOFloodOverUnicastTunnels(U16 index, U16 tunnelId) { //8
+			return OFGroup.of(0 | (index.getRaw() & 0x03ff) 
+					| (tunnelId.getRaw() << 12)
+					| (L2OverlaySubType.L2_OVERLAY_FLOOD_OVER_UNICAST_TUNNELS << 10)
+					| (OFDPAGroupType.L2_DATA_CENTER_OVERLAY << 28));
+		}
+		/**
+		 * Only bits 0-9 of index are used. Bits 10-15 are ignored.
+		 * @param index
+		 * @param tunnelId
+		 * @return
+		 */
+		public static OFGroup createL2DCOFloodOverMulticastTunnels(U16 index, U16 tunnelId) { //8
+			return OFGroup.of(0 | (index.getRaw() & 0x03ff) 
+					| (tunnelId.getRaw() << 12)
+					| (L2OverlaySubType.L2_OVERLAY_FLOOD_OVER_MULTICAST_TUNNELS << 10)
+					| (OFDPAGroupType.L2_DATA_CENTER_OVERLAY << 28));
+		}
+		/**
+		 * Only bits 0-9 of index are used. Bits 10-15 are ignored.
+		 * @param index
+		 * @param tunnelId
+		 * @return
+		 */
+		public static OFGroup createL2DCOMulticastOverUnicastTunnels(U16 index, U16 tunnelId) { //8
+			return OFGroup.of(0 | (index.getRaw() & 0x03ff) 
+					| (tunnelId.getRaw() << 12)
+					| (L2OverlaySubType.L2_OVERLAY_MULTICAST_OVER_UNICAST_TUNNELS << 10)
+					| (OFDPAGroupType.L2_DATA_CENTER_OVERLAY << 28));
+		}
+		/**
+		 * Only bits 0-9 of index are used. Bits 10-15 are ignored.
+		 * @param index
+		 * @param tunnelId
+		 * @return
+		 */
+		public static OFGroup createL2DCOMulticastOverMulticastTunnels(U16 index, U16 tunnelId) { //8
+			return OFGroup.of(0 | (index.getRaw() & 0x03ff) 
+					| (tunnelId.getRaw() << 12)
+					| (L2OverlaySubType.L2_OVERLAY_MULTICAST_OVER_MULTICAST_TUNNELS << 10)
+					| (OFDPAGroupType.L2_DATA_CENTER_OVERLAY << 28));
+		}
+		/**
+		 * Only bits 0-23 of index are used. Bits 24-31 are ignored.
+		 * @param index
+		 * @return
+		 */
+		public static OFGroup createMPLSInterfaceLabel(U32 index) { //9
+			return OFGroup.of(0 | (index.getRaw() & 0x00ffFFff) 
+					| (MPLSSubType.MPLS_INTERFACE << 24)
+					| (OFDPAGroupType.MPLS_LABEL << 28));
+		}
+		/**
+		 * Only bits 0-23 of index are used. Bits 24-31 are ignored.
+		 * @param index
+		 * @return
+		 */
+		public static OFGroup createMPLSL2VPNLabel(U32 index) { //9
+			return OFGroup.of(0 | (index.getRaw() & 0x00ffFFff) 
+					| (MPLSSubType.MPLS_L2_VPN_LABEL << 24)
+					| (OFDPAGroupType.MPLS_LABEL << 28));
+		}
+		/**
+		 * Only bits 0-23 of index are used. Bits 24-31 are ignored.
+		 * @param index
+		 * @return
+		 */
+		public static OFGroup createMPLSL3VPNLabel(U32 index) { //9
+			return OFGroup.of(0 | (index.getRaw() & 0x00ffFFff) 
+					| (MPLSSubType.MPLS_L3_VPN_LABEL << 24)
+					| (OFDPAGroupType.MPLS_LABEL << 28));
+		}
+		/**
+		 * Only bits 0-23 of index are used. Bits 24-31 are ignored.
+		 * @param index
+		 * @return
+		 */
+		public static OFGroup createMPLSTunnelLable1(U32 index) { //9
+			return OFGroup.of(0 | (index.getRaw() & 0x00ffFFff) 
+					| (MPLSSubType.MPLS_TUNNEL_LABEL_1 << 24)
+					| (OFDPAGroupType.MPLS_LABEL << 28));
+		}
+		/**
+		 * Only bits 0-23 of index are used. Bits 24-31 are ignored.
+		 * @param index
+		 * @return
+		 */
+		public static OFGroup createMPLSTunnelLabel2(U32 index) { //9
+			return OFGroup.of(0 | (index.getRaw() & 0x00ffFFff) 
+					| (MPLSSubType.MPLS_TUNNEL_LABEL_2 << 24)
+					| (OFDPAGroupType.MPLS_LABEL << 28));
+		}
+		/**
+		 * Only bits 0-23 of index are used. Bits 24-31 are ignored.
+		 * @param index
+		 * @return
+		 */
+		public static OFGroup createMPLSSwapLabel(U32 index) { //9
+			return OFGroup.of(0 | (index.getRaw() & 0x00ffFFff) 
+					| (MPLSSubType.MPLS_SWAP_LABEL << 24)
+					| (OFDPAGroupType.MPLS_LABEL << 28));
+		}
+		/**
+		 * Only bits 0-23 of index are used. Bits 24-31 are ignored.
+		 * @param index
+		 * @return
+		 */
+		public static OFGroup createMPLSForwardingFastFailover(U32 index) { //10
+			return OFGroup.of(0 | (index.getRaw() & 0x00ffFFff) 
+					| (MPLSSubType.MPLS_FAST_FAILOVER << 24)
+					| (OFDPAGroupType.MPLS_FORWARDING << 28));
+		}
+		/**
+		 * Only bits 0-23 of index are used. Bits 24-31 are ignored.
+		 * @param index
+		 * @return
+		 */
+		public static OFGroup createMPLSForwardingECMP(U32 index) { //10
+			return OFGroup.of(0 | (index.getRaw() & 0x00ffFFff) 
+					| (MPLSSubType.MPLS_ECMP << 24)
+					| (OFDPAGroupType.MPLS_FORWARDING << 28));
+		}
+		/**
+		 * Only bits 0-23 of index are used. Bits 24-31 are ignored.
+		 * @param index
+		 * @return
+		 */
+		public static OFGroup createMPLSForwardingL2Tag(U32 index) { //10
+			return OFGroup.of(0 | (index.getRaw() & 0x00ffFFff) 
+					| (MPLSSubType.MPLS_L2_TAG << 24)
+					| (OFDPAGroupType.MPLS_FORWARDING << 28));
+		}
+		public static OFGroup createL2UnfilteredInterface(OFPort p) { //11
+			return OFGroup.of(0 | p.getShortPortNumber() | (OFDPAGroupType.L2_UNFILTERED_INTERFACE << 28));
+		}
+		public static OFGroup createL2Loopback(OFPort p) { //12
+			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/OFMessageUtils.java b/src/main/java/net/floodlightcontroller/util/OFMessageUtils.java
index 9e2e676137741114cdc3fafac5a1c4d8383159a1..2914322d7834b945214858c13b675dcbda3fcfd2 100644
--- a/src/main/java/net/floodlightcontroller/util/OFMessageUtils.java
+++ b/src/main/java/net/floodlightcontroller/util/OFMessageUtils.java
@@ -1,6 +1,19 @@
 package net.floodlightcontroller.util;
 
+
+import java.util.ArrayList;
+import java.util.List;
+
+import net.floodlightcontroller.core.IOFSwitch;
+
 import org.projectfloodlight.openflow.protocol.OFMessage;
+import org.projectfloodlight.openflow.protocol.OFPacketIn;
+import org.projectfloodlight.openflow.protocol.OFPacketOut;
+import org.projectfloodlight.openflow.protocol.OFVersion;
+import org.projectfloodlight.openflow.protocol.action.OFAction;
+import org.projectfloodlight.openflow.protocol.match.MatchField;
+import org.projectfloodlight.openflow.types.OFBufferId;
+import org.projectfloodlight.openflow.types.OFPort;
 
 /**
  * Tools to help work with OFMessages.
@@ -37,4 +50,41 @@ public class OFMessageUtils {
 		OFMessage.Builder mb = b.createBuilder().setXid(a.getXid());
 		return a.equals(mb.build());
 	}
+	
+	/**
+	 * Writes an OFPacketOut message to a switch.
+	 * 
+	 * @param sw
+	 *            The switch to write the PacketOut to.
+	 * @param packetInMessage
+	 *            The corresponding PacketIn.
+	 * @param egressPort
+	 *            The switchport to output the PacketOut.
+	 */
+	public static void writePacketOutForPacketIn(IOFSwitch sw,
+			OFPacketIn packetInMessage, OFPort egressPort) {
+		
+		OFPacketOut.Builder pob = sw.getOFFactory().buildPacketOut();
+
+		// Set buffer_id, in_port, actions_len
+		pob.setBufferId(packetInMessage.getBufferId());
+		pob.setInPort(packetInMessage.getVersion().compareTo(OFVersion.OF_12) < 0 ? packetInMessage
+				.getInPort() : packetInMessage.getMatch().get(
+				MatchField.IN_PORT));
+
+		// set actions
+		List<OFAction> actions = new ArrayList<OFAction>(1);
+		actions.add(sw.getOFFactory().actions().buildOutput()
+				.setPort(egressPort).setMaxLen(0xffFFffFF).build());
+		pob.setActions(actions);
+
+		// set data - only if buffer_id == -1
+		if (packetInMessage.getBufferId() == OFBufferId.NO_BUFFER) {
+			byte[] packetData = packetInMessage.getData();
+			pob.setData(packetData);
+		}
+
+		// and write it out
+		sw.write(pob.build());
+	}
 }
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 + "]";
+	}
+}
diff --git a/src/main/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkFilter.java b/src/main/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkFilter.java
index f5ea145c4af628df9331907d7ca7ca5942969f34..cab875e333bdc047514e2ed8568fb182f1aec605 100644
--- a/src/main/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkFilter.java
+++ b/src/main/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkFilter.java
@@ -520,6 +520,12 @@ implements IFloodlightModule, IVirtualNetworkService, IOFMessageListener {
 			// add or remove entry as gateway
 			deviceAdded(device);
 		}
+		
+		@Override
+		public void deviceIPV6AddrChanged(IDevice device) {
+			//TODO
+			log.debug("IPv6 address change not handled in VirtualNetworkFilter. Device: {}", device.toString());
+		}
 
 		@Override
 		public void deviceMoved(IDevice device) {
diff --git a/src/main/resources/floodlightdefault.properties b/src/main/resources/floodlightdefault.properties
index 13ce05be948ee7b2cc60665f69491cb92fbc4f18..8c644c730d806b8140cd24cd19409d23d4ccc00c 100644
--- a/src/main/resources/floodlightdefault.properties
+++ b/src/main/resources/floodlightdefault.properties
@@ -23,12 +23,14 @@ org.sdnplatform.sync.internal.SyncManager.port=6642
 net.floodlightcontroller.forwarding.Forwarding.match=vlan, mac, ip, transport
 net.floodlightcontroller.core.internal.FloodlightProvider.openflowPort=6653
 net.floodlightcontroller.core.internal.FloodlightProvider.role=ACTIVE
-net.floodlightcontroller.core.internal.OFSwitchManager.addDefaultSendToControllerFlowInTables='{"all":"0,1,2"}'
+net.floodlightcontroller.core.internal.OFSwitchManager.defaultMaxTablesToReceiveTableMissFlow=2
+net.floodlightcontroller.core.internal.OFSwitchManager.maxTablesToReceiveTableMissFlowPerDpid={"00:00:00:00:00:00:00:01":"1","2":"1"}
 net.floodlightcontroller.core.internal.OFSwitchManager.clearTablesOnInitialHandshakeAsMaster=YES
 net.floodlightcontroller.core.internal.OFSwitchManager.clearTablesOnEachTransitionToMaster=YES
 net.floodlightcontroller.core.internal.OFSwitchManager.keyStorePath=/path/to/your/keystore-file.jks
 net.floodlightcontroller.core.internal.OFSwitchManager.keyStorePassword=your-keystore-password
 net.floodlightcontroller.core.internal.OFSwitchManager.useSsl=NO
+net.floodlightcontroller.core.internal.OFSwitchManager.supportedOpenFlowVersions=1.0, 1.1, 1.2, 1.3, 1.4
 net.floodlightcontroller.restserver.RestApiServer.keyStorePath=/path/to/your/keystore-file.jks
 net.floodlightcontroller.restserver.RestApiServer.keyStorePassword=your-keystore-password
 net.floodlightcontroller.restserver.RestApiServer.httpsNeedClientAuthentication=NO
diff --git a/src/main/resources/logback-test.xml b/src/main/resources/logback-test.xml
index 2c8ca28ccbefd41c21583decb480458dc076366a..21c48f39c6a6b94e501b3bafd9a97528b25f2b02 100644
--- a/src/main/resources/logback-test.xml
+++ b/src/main/resources/logback-test.xml
@@ -17,7 +17,6 @@
   <logger name="net.floodlightcontroller.devicemanager" level="INFO"></logger>
   <logger name="net.floodlightcontroller.packet" level="INFO"></logger>
   <logger name="net.floodlightcontroller.forwarding" level="INFO"></logger>
-  <logger name="net.floodlightcontroller.routing" level="INFO"></logger>
   <logger name="net.floodlightcontroller.core.internal" level="INFO"></logger>
   <logger level="INFO" name="net.floodlightcontroller.learningswitch"></logger>
   <logger level="INFO" name="org.projectfloodlight.openflow"></logger>
diff --git a/src/test/java/net/floodlightcontroller/accesscontrollist/ACLTest.java b/src/test/java/net/floodlightcontroller/accesscontrollist/ACLTest.java
index 230f1e93436e498cfafd4789eb0811c80fce1abc..f9c4d0487f11a7eca2dfeeca41f005757c15b7b4 100644
--- a/src/test/java/net/floodlightcontroller/accesscontrollist/ACLTest.java
+++ b/src/test/java/net/floodlightcontroller/accesscontrollist/ACLTest.java
@@ -51,8 +51,6 @@ import net.floodlightcontroller.devicemanager.IDeviceService;
 import net.floodlightcontroller.devicemanager.IEntityClassifierService;
 import net.floodlightcontroller.devicemanager.internal.DefaultEntityClassifier;
 import net.floodlightcontroller.devicemanager.test.MockDeviceManager;
-import net.floodlightcontroller.packet.Ethernet;
-import net.floodlightcontroller.packet.IPv4;
 import net.floodlightcontroller.restserver.IRestApiService;
 import net.floodlightcontroller.restserver.RestApiServer;
 import net.floodlightcontroller.staticflowentry.StaticFlowEntryPusher;
@@ -69,7 +67,11 @@ import org.junit.Test;
 import org.projectfloodlight.openflow.protocol.OFFactories;
 import org.projectfloodlight.openflow.protocol.OFVersion;
 import org.projectfloodlight.openflow.types.DatapathId;
+import org.projectfloodlight.openflow.types.IPv4Address;
+import org.projectfloodlight.openflow.types.IPv6Address;
+import org.projectfloodlight.openflow.types.MacAddress;
 import org.projectfloodlight.openflow.types.OFPort;
+import org.projectfloodlight.openflow.types.VlanVid;
 
 public class ACLTest extends FloodlightTestCase {
 
@@ -177,14 +179,12 @@ public class ACLTest extends FloodlightTestCase {
 		Map<String, Object> row;
 
 		// a new AP[dpid:00:00:00:00:00:00:00:01 port:1 ip:10.0.0.1] appears
-		deviceManager.learnEntity(
-				Ethernet.toLong(Ethernet.toMACAddress("00:00:00:00:00:01")),
-				null, IPv4.toIPv4Address("10.0.0.1"), 1L, 1);
+		deviceManager.learnEntity(MacAddress.of("00:00:00:00:00:01"),
+				VlanVid.ZERO, IPv4Address.of("10.0.0.1"), IPv6Address.NONE, DatapathId.of(1), OFPort.of(1));
 		
 		// a new AP[dpid:00:00:00:00:00:00:00:02 port:1 ip:10.0.0.3] appears
-		deviceManager.learnEntity(
-				Ethernet.toLong(Ethernet.toMACAddress("00:00:00:00:00:03")),
-				null, IPv4.toIPv4Address("10.0.0.3"), 2L, 1);
+		deviceManager.learnEntity(MacAddress.of("00:00:00:00:00:03"),
+				VlanVid.ZERO, IPv4Address.of("10.0.0.3"), IPv6Address.NONE, DatapathId.of(2), OFPort.of(1));
 		
 		// rule1 indicates host(10.0.0.0/28) can not access TCP port 80 in host(10.0.0.254/32)
 		rule1 = new ACLRule();
@@ -344,9 +344,8 @@ public class ACLTest extends FloodlightTestCase {
 		assertEquals(acl.getRules().size(), 1);
 		
 		// a new AP[dpid:00:00:00:00:00:00:00:01 port:1 ip:10.0.0.1] appears
-		deviceManager.learnEntity(
-				Ethernet.toLong(Ethernet.toMACAddress("00:00:00:00:00:01")),
-				null, IPv4.toIPv4Address("10.0.0.1"), 1L, 1);
+		deviceManager.learnEntity(MacAddress.of("00:00:00:00:00:01"),
+				VlanVid.ZERO, IPv4Address.of("10.0.0.1"), IPv6Address.NONE, DatapathId.of(1), OFPort.of(1));
 		
 		resultSet = storageService.getRow(
 				StaticFlowEntryPusher.TABLE_NAME, "ACLRule_1_00:00:00:00:00:00:00:01");
@@ -364,9 +363,8 @@ public class ACLTest extends FloodlightTestCase {
 		}
 		
 		// a new AP[dpid:00:00:00:00:00:00:00:01 port:2 ip:10.0.0.2] appears
-		deviceManager.learnEntity(
-				Ethernet.toLong(Ethernet.toMACAddress("00:00:00:00:00:02")),
-				null, IPv4.toIPv4Address("10.0.0.2"), 1L, 2);
+		deviceManager.learnEntity(MacAddress.of("00:00:00:00:00:02"),
+				VlanVid.ZERO, IPv4Address.of("10.0.0.2"), IPv6Address.NONE, DatapathId.of(1), OFPort.of(2));
 		
 		resultSet = storageService.getRow(
 				StaticFlowEntryPusher.TABLE_NAME, "ACLRule_1_00:00:00:00:00:00:00:01");
@@ -392,9 +390,8 @@ public class ACLTest extends FloodlightTestCase {
 		assertEquals(acl.getRules().size(), 2);
 
 		// a new AP[dpid:00:00:00:00:00:00:00:02 port:1 ip:10.0.0.3] appears
-		deviceManager.learnEntity(
-				Ethernet.toLong(Ethernet.toMACAddress("00:00:00:00:00:03")),
-				null, IPv4.toIPv4Address("10.0.0.3"), 2L, 1);
+		deviceManager.learnEntity(MacAddress.of("00:00:00:00:00:03"),
+				VlanVid.ZERO, IPv4Address.of("10.0.0.3"), IPv6Address.NONE, DatapathId.of(2), OFPort.of(1));
 		
 		resultSet = storageService.getRow(
 				StaticFlowEntryPusher.TABLE_NAME, "ACLRule_2_00:00:00:00:00:00:00:02");
@@ -428,9 +425,8 @@ public class ACLTest extends FloodlightTestCase {
 		Map<String, Object> row;
 	
 		// a new AP[dpid:00:00:00:00:00:00:00:01 port:1] appears
-		deviceManager.learnEntity(
-				Ethernet.toLong(Ethernet.toMACAddress("00:00:00:00:00:01")),
-				null, null, 1L, 1);
+		deviceManager.learnEntity(MacAddress.of("00:00:00:00:00:01"),
+				VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(1), OFPort.of(1));
 		
 		// rule1 indicates host(10.0.0.0/28) can not access TCP port 80 in host(10.0.0.254/32)
 		rule1 = new ACLRule();
@@ -455,9 +451,8 @@ public class ACLTest extends FloodlightTestCase {
 		assertEquals(it.hasNext(), false);
 		
 		// a new AP[dpid:00:00:00:00:00:00:00:01 port:1 ip:10.0.0.1] appears
-		deviceManager.learnEntity(
-				Ethernet.toLong(Ethernet.toMACAddress("00:00:00:00:00:01")),
-				null, IPv4.toIPv4Address("10.0.0.1"), 1L, 1);
+		deviceManager.learnEntity(MacAddress.of("00:00:00:00:00:01"),
+				VlanVid.ZERO, IPv4Address.of("10.0.0.1"), IPv6Address.NONE, DatapathId.of(1), OFPort.of(1));
 		
 		resultSet = storageService.getRow(
 				StaticFlowEntryPusher.TABLE_NAME, "ACLRule_1_00:00:00:00:00:00:00:01");
@@ -491,14 +486,12 @@ public class ACLTest extends FloodlightTestCase {
 		Map<String, Object> row;
 
 		// a new AP[dpid:00:00:00:00:00:00:00:01 port:1 ip:10.0.0.1] appears
-		deviceManager.learnEntity(
-				Ethernet.toLong(Ethernet.toMACAddress("00:00:00:00:00:01")),
-				null, IPv4.toIPv4Address("10.0.0.1"), 1L, 1);
+		deviceManager.learnEntity(MacAddress.of("00:00:00:00:00:01"),
+				VlanVid.ZERO, IPv4Address.of("10.0.0.1"), IPv6Address.NONE, DatapathId.of(1), OFPort.of(1));
 		
 		// a new AP[dpid:00:00:00:00:00:00:00:02 port:1 ip:10.0.0.3] appears
-		deviceManager.learnEntity(
-				Ethernet.toLong(Ethernet.toMACAddress("00:00:00:00:00:03")),
-				null, IPv4.toIPv4Address("10.0.0.3"), 2L, 1);
+		deviceManager.learnEntity(MacAddress.of("00:00:00:00:00:03"),
+				VlanVid.ZERO, IPv4Address.of("10.0.0.3"), IPv6Address.NONE, DatapathId.of(2), OFPort.of(1));
 		
 		// rule1 indicates host(10.0.0.0/28) can not access TCP port 80 in host(10.0.0.254/32)
 		rule1 = new ACLRule();
@@ -579,14 +572,12 @@ public class ACLTest extends FloodlightTestCase {
 		Map<String, Object> row;
 
 		// a new AP[dpid:00:00:00:00:00:00:00:01 port:1 ip:10.0.0.1] appears
-		deviceManager.learnEntity(
-				Ethernet.toLong(Ethernet.toMACAddress("00:00:00:00:00:01")),
-				null, IPv4.toIPv4Address("10.0.0.1"), 1L, 1);
+		deviceManager.learnEntity(MacAddress.of("00:00:00:00:00:01"),
+				VlanVid.ZERO, IPv4Address.of("10.0.0.1"), IPv6Address.NONE, DatapathId.of(1), OFPort.of(1));
 		
 		// a new AP[dpid:00:00:00:00:00:00:00:02 port:1 ip:10.0.0.3] appears
-		deviceManager.learnEntity(
-				Ethernet.toLong(Ethernet.toMACAddress("00:00:00:00:00:03")),
-				null, IPv4.toIPv4Address("10.0.0.3"), 2L, 1);
+		deviceManager.learnEntity(MacAddress.of("00:00:00:00:00:03"),
+				VlanVid.ZERO, IPv4Address.of("10.0.0.3"), IPv6Address.NONE, DatapathId.of(2), OFPort.of(1));
 		
 		// rule1 indicates host(10.0.0.0/28) can not access TCP port 80 in host(10.0.0.254/32)
 		rule1 = new ACLRule();
diff --git a/src/test/java/net/floodlightcontroller/core/OFSwitchBaseTest.java b/src/test/java/net/floodlightcontroller/core/OFSwitchBaseTest.java
index fc9a0c3c3b0a7c4320eafca78e601ae1d8205495..24db287af6da5c28214280f1a240991dafc27e55 100644
--- a/src/test/java/net/floodlightcontroller/core/OFSwitchBaseTest.java
+++ b/src/test/java/net/floodlightcontroller/core/OFSwitchBaseTest.java
@@ -154,9 +154,9 @@ public class OFSwitchBaseTest {
         switches.put(sw.getId(), sw);
         reset(switchManager);
         //expect(switchManager.getSwitch(sw.getId())).andReturn(sw).anyTimes();
+        setUpPorts();
     }
 
-    @Before
     public void setUpPorts() {
         OFPortDesc.Builder pdb = OFFactories.getFactory(OFVersion.OF_13).buildPortDesc();
         // p1a is disabled
@@ -1394,6 +1394,8 @@ public class OFSwitchBaseTest {
 
         reset(switchManager);
         expect(switchManager.isCategoryRegistered(category)).andReturn(true);
+        switchManager.handleOutgoingMessage(sw, testMessage);
+        expectLastCall();
         replay(switchManager);
 
         sw.write(testMessage, category);
diff --git a/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java b/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java
index 7e0d2ceac1532ffff217e8990c49d7806b38cdbb..669876e7904ba16f5db21284869fc387724be6d3 100644
--- a/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java
+++ b/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java
@@ -46,7 +46,6 @@ import net.floodlightcontroller.debugevent.IDebugEventService;
 import net.floodlightcontroller.packet.ARP;
 import net.floodlightcontroller.packet.Ethernet;
 import net.floodlightcontroller.packet.IPacket;
-import net.floodlightcontroller.packet.IPv4;
 import net.floodlightcontroller.perfmon.IPktInProcessingTimeService;
 import net.floodlightcontroller.perfmon.PktInProcessingTime;
 import net.floodlightcontroller.restserver.IRestApiService;
@@ -74,6 +73,8 @@ import org.projectfloodlight.openflow.protocol.OFPacketInReason;
 import org.projectfloodlight.openflow.protocol.OFVersion;
 import org.projectfloodlight.openflow.types.DatapathId;
 import org.projectfloodlight.openflow.types.EthType;
+import org.projectfloodlight.openflow.types.IPv4Address;
+import org.projectfloodlight.openflow.types.MacAddress;
 import org.projectfloodlight.openflow.types.OFBufferId;
 import org.projectfloodlight.openflow.types.OFPort;
 import org.projectfloodlight.openflow.protocol.OFPortDesc;
@@ -180,10 +181,10 @@ public class ControllerTest extends FloodlightTestCase {
                 .setHardwareAddressLength((byte) 6)
                 .setProtocolAddressLength((byte) 4)
                 .setOpCode(ARP.OP_REPLY)
-                .setSenderHardwareAddress(Ethernet.toMACAddress("00:44:33:22:11:00"))
-                .setSenderProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.1"))
-                .setTargetHardwareAddress(Ethernet.toMACAddress("00:11:22:33:44:55"))
-                .setTargetProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.2")));
+                .setSenderHardwareAddress(MacAddress.of("00:44:33:22:11:00"))
+                .setSenderProtocolAddress(IPv4Address.of("192.168.1.1"))
+                .setTargetHardwareAddress(MacAddress.of("00:11:22:33:44:55"))
+                .setTargetProtocolAddress(IPv4Address.of("192.168.1.2")));
         byte[] testPacketSerialized = testPacket.serialize();
 
         // The specific factory can be obtained from the switch, but we don't have one
@@ -243,7 +244,7 @@ public class ControllerTest extends FloodlightTestCase {
         expect(sw.getSwitchDescription()).andReturn(description).atLeastOnce();
         expect(sw.getBuffers())
                 .andReturn(featuresReply.getNBuffers()).atLeastOnce();
-        expect(sw.getTables())
+        expect(sw.getNumTables())
                 .andReturn(featuresReply.getNTables()).atLeastOnce();
         expect(sw.getCapabilities())
                 .andReturn(featuresReply.getCapabilities()).atLeastOnce();
diff --git a/src/test/java/net/floodlightcontroller/core/internal/OFChannelHandlerVer10Test.java b/src/test/java/net/floodlightcontroller/core/internal/OFChannelHandlerVer10Test.java
index 038639f79ff45cd566c5f92c3c684d377cb2f49d..96147b02eb52b4323be4f804ffad6014c552809b 100644
--- a/src/test/java/net/floodlightcontroller/core/internal/OFChannelHandlerVer10Test.java
+++ b/src/test/java/net/floodlightcontroller/core/internal/OFChannelHandlerVer10Test.java
@@ -35,12 +35,14 @@ import org.jboss.netty.util.Timer;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+
 import net.floodlightcontroller.core.IOFConnectionBackend;
 import net.floodlightcontroller.core.OFConnectionCounters;
 import net.floodlightcontroller.core.internal.OpenflowPipelineFactory.PipelineHandler;
 import net.floodlightcontroller.core.internal.OpenflowPipelineFactory.PipelineHandshakeTimeout;
 import net.floodlightcontroller.debugcounter.DebugCounterServiceImpl;
 import net.floodlightcontroller.debugcounter.IDebugCounterService;
+
 import org.projectfloodlight.openflow.protocol.OFActionType;
 import org.projectfloodlight.openflow.protocol.OFBarrierReply;
 import org.projectfloodlight.openflow.protocol.OFCapabilities;
@@ -63,6 +65,7 @@ import org.projectfloodlight.openflow.protocol.OFType;
 import org.projectfloodlight.openflow.protocol.OFVersion;
 import org.projectfloodlight.openflow.types.DatapathId;
 import org.projectfloodlight.openflow.types.OFPort;
+import org.projectfloodlight.openflow.types.U32;
 
 import com.google.common.collect.ImmutableList;
 
@@ -92,7 +95,6 @@ public class OFChannelHandlerVer10Test {
     private Capture<IOFConnectionBackend> newConnection;
     private Capture<OFFeaturesReply> newFeaturesReply;
 
-    @Before
     public void setUpFeaturesReply() {
        portDesc = factory.buildPortDesc()
                 .setName("Eth1")
@@ -111,6 +113,7 @@ public class OFChannelHandlerVer10Test {
 
     @Before
     public void setUp() throws Exception {
+    	setUpFeaturesReply();
         switchManager = createMock(IOFSwitchManager.class);
         connectionListener = createMock(IOFConnectionListener.class);
         newConnectionListener = createMock(INewOFConnectionListener.class);
@@ -137,7 +140,7 @@ public class OFChannelHandlerVer10Test {
         replay(switchManager);
         handler = new OFChannelHandler(switchManager, newConnectionListener,
                                        pipeline, debugCounterService,
-                                       timer);
+                                       timer, Collections.singletonList(U32.of(0)), OFFactories.getFactory(OFVersion.OF_14));
         verify(switchManager);
         reset(switchManager);
 
diff --git a/src/test/java/net/floodlightcontroller/core/internal/OFChannelHandlerVer13Test.java b/src/test/java/net/floodlightcontroller/core/internal/OFChannelHandlerVer13Test.java
index 0703ef8c49a8ecdd052c59c96f176bf16a516ab8..9b9b6ce897bf00c210326fed7d49e315500e11eb 100644
--- a/src/test/java/net/floodlightcontroller/core/internal/OFChannelHandlerVer13Test.java
+++ b/src/test/java/net/floodlightcontroller/core/internal/OFChannelHandlerVer13Test.java
@@ -35,12 +35,14 @@ import org.jboss.netty.util.Timer;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+
 import net.floodlightcontroller.core.IOFConnectionBackend;
 import net.floodlightcontroller.core.OFConnectionCounters;
 import net.floodlightcontroller.core.internal.OpenflowPipelineFactory.PipelineHandler;
 import net.floodlightcontroller.core.internal.OpenflowPipelineFactory.PipelineHandshakeTimeout;
 import net.floodlightcontroller.debugcounter.DebugCounterServiceImpl;
 import net.floodlightcontroller.debugcounter.IDebugCounterService;
+
 import org.projectfloodlight.openflow.protocol.OFBarrierReply;
 import org.projectfloodlight.openflow.protocol.OFBsnSetAuxCxnsReply;
 import org.projectfloodlight.openflow.protocol.OFCapabilities;
@@ -64,6 +66,7 @@ import org.projectfloodlight.openflow.protocol.OFVersion;
 import org.projectfloodlight.openflow.types.DatapathId;
 import org.projectfloodlight.openflow.types.OFAuxId;
 import org.projectfloodlight.openflow.types.OFPort;
+import org.projectfloodlight.openflow.types.U32;
 
 import com.google.common.collect.ImmutableList;
 
@@ -96,7 +99,6 @@ public class OFChannelHandlerVer13Test {
 
     private Capture<OFFeaturesReply> newFeaturesReply;
 
-    @Before
     public void setUpFeaturesReply() {
         portDesc = factory.buildPortDesc()
                 .setName("Eth1")
@@ -113,6 +115,7 @@ public class OFChannelHandlerVer13Test {
 
     @Before
     public void setUp() throws Exception {
+    	setUpFeaturesReply();
         switchManager = createMock(IOFSwitchManager.class);
         connectionListener = createMock(IOFConnectionListener.class);
         newConnectionListener = createMock(INewOFConnectionListener.class);
@@ -139,7 +142,9 @@ public class OFChannelHandlerVer13Test {
         expect(switchManager.getCounters()).andReturn(counters).anyTimes();
         replay(switchManager);
         handler = new OFChannelHandler(switchManager, newConnectionListener,
-                                       pipeline, debugCounterService, timer);
+                                       pipeline, debugCounterService, /* 62 is OF versions 1.0 thru 1.4 in decimal */
+                                       timer, Collections.singletonList(U32.of(62)), OFFactories.getFactory(OFVersion.OF_14));
+
         verify(switchManager);
         reset(switchManager);
 
diff --git a/src/test/java/net/floodlightcontroller/core/internal/OFSwitchHandlerTestBase.java b/src/test/java/net/floodlightcontroller/core/internal/OFSwitchHandlerTestBase.java
index e7590df1c838b059a9bdfc33f4387bfc8aecc66b..675873a5eeac46c7e8db6cef30e31ea02680fd08 100644
--- a/src/test/java/net/floodlightcontroller/core/internal/OFSwitchHandlerTestBase.java
+++ b/src/test/java/net/floodlightcontroller/core/internal/OFSwitchHandlerTestBase.java
@@ -48,6 +48,7 @@ import net.floodlightcontroller.debugcounter.IDebugCounterService;
 
 import org.projectfloodlight.openflow.protocol.OFBadActionCode;
 import org.projectfloodlight.openflow.protocol.OFBadRequestCode;
+import org.projectfloodlight.openflow.protocol.OFBarrierReply;
 import org.projectfloodlight.openflow.protocol.OFControllerRole;
 import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
 import org.projectfloodlight.openflow.protocol.OFErrorMsg;
@@ -66,6 +67,7 @@ import org.projectfloodlight.openflow.protocol.OFStatsType;
 import org.projectfloodlight.openflow.protocol.OFTableFeatureProp;
 import org.projectfloodlight.openflow.protocol.OFTableFeaturesStatsReply;
 import org.projectfloodlight.openflow.protocol.OFType;
+import org.projectfloodlight.openflow.protocol.OFVersion;
 import org.projectfloodlight.openflow.protocol.match.Match;
 import org.projectfloodlight.openflow.types.DatapathId;
 import org.projectfloodlight.openflow.types.OFAuxId;
@@ -81,1085 +83,1119 @@ import com.google.common.collect.ImmutableList;
 
 
 public abstract class OFSwitchHandlerTestBase {
-    protected static final DatapathId dpid = DatapathId.of(0x42L);
-
-    protected IOFSwitchManager switchManager;
-    protected RoleManager roleManager;
-
-    private IDebugCounterService debugCounterService;
-    protected OFSwitchHandshakeHandler switchHandler;
-    protected MockOFConnection connection;
-    // Use a 1.0 factory for the 1.0 test
-    protected final OFFactory factory = getFactory();
-
-    protected OFFeaturesReply featuresReply;
-    protected List<IAppHandshakePluginFactory> plugins;
-
-    private HashSet<Long> seenXids = null;
-    protected IOFSwitchBackend sw;
-    private Timer timer;
-    private TestHandshakePlugin handshakePlugin;
-
-    private class TestHandshakePlugin extends OFSwitchAppHandshakePlugin {
-        protected TestHandshakePlugin(PluginResult defaultResult, int timeoutS) {
-            super(defaultResult, timeoutS);
-        }
-
-        @Override
-        protected void processOFMessage(OFMessage m) {
-        }
-
-        @Override
-        protected void enterPlugin() {
-        }
-    }
-
-    @Before
-    public void setUpFeaturesReply() {
-        getFeaturesReply();
-        this.featuresReply = getFeaturesReply();
-
-        // Plugin set
-        IAppHandshakePluginFactory factory = createMock(IAppHandshakePluginFactory.class);
-        PluginResult result = new PluginResult(PluginResultType.QUARANTINE, "test quarantine");
-        handshakePlugin = new TestHandshakePlugin(result, 5);
-        expect(factory.createPlugin()).andReturn(handshakePlugin).anyTimes();
-        replay(factory);
-        plugins = ImmutableList.of(factory);
-    }
-
-
-    @Before
-    public void setUp() throws Exception {
-        switchManager = createMock(IOFSwitchManager.class);
-        roleManager = createMock(RoleManager.class);
-        sw = createMock(IOFSwitchBackend.class);
-        timer = createMock(Timer.class);
-        expect(timer.newTimeout(anyObject(TimerTask.class), anyLong(), anyObject(TimeUnit.class))).andReturn(EasyMock.createNiceMock(Timeout.class));
-        replay(timer);
-        seenXids = null;
-
-        // TODO: should mock IDebugCounterService and make sure
-        // the expected counters are updated.
-        debugCounterService = new DebugCounterServiceImpl();
-        SwitchManagerCounters counters =
-                new SwitchManagerCounters(debugCounterService);
-        expect(switchManager.getCounters()).andReturn(counters).anyTimes();
-        replay(switchManager);
-        connection = new MockOFConnection(featuresReply.getDatapathId(), OFAuxId.MAIN);
-        switchHandler = new OFSwitchHandshakeHandler(connection, featuresReply, switchManager, roleManager, timer);
-        
-        // replay sw. Reset it if you need more specific behavior
-        replay(sw);
-    }
-
-
-    @After
-    public void tearDown() {
-        verifyAll();
-    }
-
-    private void verifyAll() {
-        assertThat("Unexpected messages have been captured",
-                   connection.getMessages(),
-                    Matchers.empty());
-        // verify all mocks.
-        verify(sw);
-    }
-
-    void verifyUniqueXids(OFMessage... msgs) {
-        verifyUniqueXids(Arrays.asList(msgs));
-    }
-
-    /** make sure that the transaction ids in the given messages are
-     * not 0 and differ between each other.
-     * While it's not a defect per se if the xids are we want to ensure
-     * we use different ones for each message we send.
-     */
-    void verifyUniqueXids(List<OFMessage> msgs) {
-        if (seenXids == null)
-            seenXids = new HashSet<Long>();
-        for (OFMessage m: msgs)  {
-            long xid = m.getXid();
-            assertTrue("Xid in messags is 0", xid != 0);
-            assertFalse("Xid " + xid + " has already been used",
-                        seenXids.contains(xid));
-            seenXids.add(xid);
-        }
-    }
-
-
-    /*************************** abstract phases / utilities to be filled in by the subclasses */
-
-    // Factory + messages
-
-    /** @return the version-appropriate factory */
-    public abstract OFFactory getFactory();
-
-    /**
-     * @return a version appropriate features reply (different in 1.3 because it
-     * doesn't have ports)
-     */
-    abstract OFFeaturesReply getFeaturesReply();
-    /** @return the class that's used for role requests/replies (OFNiciraRoleRequest vs.
-     *  OFRequest)
-     */
-
-    /// Role differences
-
-    abstract Class<?> getRoleRequestClass();
-    /** Verify that the given OFMessage is a correct RoleRequest message
-     * for the given role using the given xid (for the version).
-     */
-    public abstract void verifyRoleRequest(OFMessage m,
-                                   OFControllerRole expectedRole);
-    /** Return a RoleReply message for the given role */
-    protected abstract OFMessage getRoleReply(long xid, OFControllerRole role);
-
-    /// Difference in the handshake sequence
-
-    /** OF1.3 has the PortDescStatsRequest, OF1.0 not */
-    abstract void moveToPreConfigReply() throws Exception;
-    /**
-     * Move the channel from scratch to WaitAppHandshakeState
-     * Different for OF1.0 and OF1.3 because of GenTables.
-     * @throws Exception
-     */
-    @Test
-    public abstract void moveToWaitAppHandshakeState() throws Exception;
-
-    /**
-     * Move the channel from scratch to WaitSwitchDriverSubHandshake
-     * Different for OF1.0 and OF1.3 because of GenTables.
-     * @throws Exception
-     */
-    @Test
-    public abstract void moveToWaitSwitchDriverSubHandshake() throws Exception;
-
-    /**
-     * Move the channel from scratch to WaitInitialRole
-     * Different for OF1.0 and OF1.3 because of Controller Connections.
-     * @throws Exception
-     */
-    @Test
-    public abstract void moveToWaitInitialRole() throws Exception;
-
-    /*******************************************************************************************/
-
-
-    /** Move the channel from scratch to INIT state
+	protected static final DatapathId dpid = DatapathId.of(0x42L);
+
+	protected IOFSwitchManager switchManager;
+	protected RoleManager roleManager;
+
+	private IDebugCounterService debugCounterService;
+	protected OFSwitchHandshakeHandler switchHandler;
+	protected MockOFConnection connection;
+	// Use a 1.0 factory for the 1.0 test
+	protected final OFFactory factory = getFactory();
+
+	protected OFFeaturesReply featuresReply;
+	protected List<IAppHandshakePluginFactory> plugins;
+
+	private HashSet<Long> seenXids = null;
+	protected IOFSwitchBackend sw;
+	private Timer timer;
+	private TestHandshakePlugin handshakePlugin;
+
+	private class TestHandshakePlugin extends OFSwitchAppHandshakePlugin {
+		protected TestHandshakePlugin(PluginResult defaultResult, int timeoutS) {
+			super(defaultResult, timeoutS);
+		}
+
+		@Override
+		protected void processOFMessage(OFMessage m) {
+		}
+
+		@Override
+		protected void enterPlugin() {
+		}
+	}
+
+	
+	public void setUpFeaturesReply() {
+		getFeaturesReply();
+		this.featuresReply = getFeaturesReply();
+
+		// Plugin set
+		IAppHandshakePluginFactory factory = createMock(IAppHandshakePluginFactory.class);
+		PluginResult result = new PluginResult(PluginResultType.QUARANTINE, "test quarantine");
+		handshakePlugin = new TestHandshakePlugin(result, 5);
+		expect(factory.createPlugin()).andReturn(handshakePlugin).anyTimes();
+		replay(factory);
+		plugins = ImmutableList.of(factory);
+	}
+
+
+	@Before
+	public void setUp() throws Exception {
+		/*
+		 * This needs to be called explicitly to ensure the featuresReply is not null.
+		 * Otherwise, there is no guarantee @Before will for setUpFeaturesReply() will
+		 * call that function before our @Before setUp() here.
+		 */
+		setUpFeaturesReply(); 
+		switchManager = createMock(IOFSwitchManager.class);
+		roleManager = createMock(RoleManager.class);
+		sw = createMock(IOFSwitchBackend.class);
+		timer = createMock(Timer.class);
+		expect(timer.newTimeout(anyObject(TimerTask.class), anyLong(), anyObject(TimeUnit.class))).andReturn(EasyMock.createNiceMock(Timeout.class));
+		replay(timer);
+		seenXids = null;
+
+		// TODO: should mock IDebugCounterService and make sure
+		// the expected counters are updated.
+		debugCounterService = new DebugCounterServiceImpl();
+		SwitchManagerCounters counters =
+				new SwitchManagerCounters(debugCounterService);
+		expect(switchManager.getCounters()).andReturn(counters).anyTimes();
+		replay(switchManager);
+		connection = new MockOFConnection(featuresReply.getDatapathId(), OFAuxId.MAIN);
+		switchHandler = new OFSwitchHandshakeHandler(connection, featuresReply, switchManager, roleManager, timer);
+
+		// replay sw. Reset it if you need more specific behavior
+		replay(sw);
+	}
+
+
+	@After
+	public void tearDown() {
+		verifyAll();
+	}
+
+	private void verifyAll() {
+		assertThat("Unexpected messages have been captured",
+				connection.getMessages(),
+				Matchers.empty());
+		// verify all mocks.
+		verify(sw);
+	}
+
+	void verifyUniqueXids(OFMessage... msgs) {
+		verifyUniqueXids(Arrays.asList(msgs));
+	}
+
+	/** make sure that the transaction ids in the given messages are
+	 * not 0 and differ between each other.
+	 * While it's not a defect per se if the xids are we want to ensure
+	 * we use different ones for each message we send.
+	 */
+	void verifyUniqueXids(List<OFMessage> msgs) {
+		if (seenXids == null)
+			seenXids = new HashSet<Long>();
+		for (OFMessage m: msgs)  {
+			long xid = m.getXid();
+			assertTrue("Xid in messags is 0", xid != 0);
+			assertFalse("Xid " + xid + " has already been used",
+					seenXids.contains(xid));
+			seenXids.add(xid);
+		}
+	}
+
+
+	/*************************** abstract phases / utilities to be filled in by the subclasses */
+
+	// Factory + messages
+
+	/** @return the version-appropriate factory */
+	public abstract OFFactory getFactory();
+
+	/**
+	 * @return a version appropriate features reply (different in 1.3 because it
+	 * doesn't have ports)
+	 */
+	abstract OFFeaturesReply getFeaturesReply();
+	/** @return the class that's used for role requests/replies (OFNiciraRoleRequest vs.
+	 *  OFRequest)
+	 */
+
+	/// Role differences
+
+	abstract Class<?> getRoleRequestClass();
+	/** Verify that the given OFMessage is a correct RoleRequest message
+	 * for the given role using the given xid (for the version).
+	 */
+	public abstract void verifyRoleRequest(OFMessage m,
+			OFControllerRole expectedRole);
+	/** Return a RoleReply message for the given role */
+	protected abstract OFMessage getRoleReply(long xid, OFControllerRole role);
+
+	/// Difference in the handshake sequence
+
+	/** OF1.3 has the PortDescStatsRequest, OF1.0 not */
+	abstract void moveToPreConfigReply() throws Exception;
+	/**
+	 * Move the channel from scratch to WaitAppHandshakeState
+	 * Different for OF1.0 and OF1.3 because of GenTables.
+	 * @throws Exception
+	 */
+	@Test
+	public abstract void moveToWaitAppHandshakeState() throws Exception;
+
+	/**
+	 * Move the channel from scratch to WaitSwitchDriverSubHandshake
+	 * Different for OF1.0 and OF1.3 because of GenTables.
+	 * @throws Exception
+	 */
+	@Test
+	public abstract void moveToWaitSwitchDriverSubHandshake() throws Exception;
+
+	/**
+	 * Move the channel from scratch to WaitInitialRole
+	 * Different for OF1.0 and OF1.3 because of Controller Connections.
+	 * @throws Exception
+	 */
+	@Test
+	public abstract void moveToWaitInitialRole() throws Exception;
+
+	/*******************************************************************************************/
+
+
+	/** Move the channel from scratch to INIT state
         This occurs upon creation of the switch handler
-     */
-    @Test
-    public void testInitState() throws Exception {
-        assertThat(connection.getListener(), notNullValue());
-        assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.InitState.class));
-    }
-
-
-    /** Move the channel from scratch to WAIT_CONFIG_REPLY state
-     * adds testing for beginHandshake() which moves the state from
-     * InitState to WaitConfigReply.
-     */
-    @Test
-    public void moveToWaitConfigReply() throws Exception {
-        moveToPreConfigReply();
-
-        List<OFMessage> msgs = connection.getMessages();
-        assertEquals(3, msgs.size());
-        assertEquals(OFType.SET_CONFIG, msgs.get(0).getType());
-        OFSetConfig sc = (OFSetConfig)msgs.get(0);
-        assertEquals(0xffff, sc.getMissSendLen());
-        assertEquals(OFType.BARRIER_REQUEST, msgs.get(1).getType());
-        assertEquals(OFType.GET_CONFIG_REQUEST, msgs.get(2).getType());
-        verifyUniqueXids(msgs);
-        msgs.clear();
-        assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitConfigReplyState.class));
-        verifyAll();
-    }
-
-
-
-    /** Move the channel from scratch to WAIT_DESCRIPTION_STAT_REPLY state
-     * Builds on moveToWaitConfigReply()
-     * adds testing for WAIT_CONFIG_REPLY state
-     */
-    @Test
-    public void moveToWaitDescriptionStatReply() throws Exception {
-        moveToWaitConfigReply();
-
-        connection.clearMessages();
-        OFGetConfigReply cr = factory.buildGetConfigReply()
-                .setMissSendLen(0xFFFF)
-                .build();
-
-        switchHandler.processOFMessage(cr);
-
-        OFMessage msg = connection.retrieveMessage();
-        assertEquals(OFType.STATS_REQUEST, msg.getType());
-        OFStatsRequest<?> sr = (OFStatsRequest<?>)msg;
-        assertEquals(OFStatsType.DESC, sr.getStatsType());
-        verifyUniqueXids(msg);
-        assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitDescriptionStatReplyState.class));
-    }
-
-    protected OFDescStatsReply createDescriptionStatsReply() {
-        OFDescStatsReply statsReply = factory.buildDescStatsReply()
-                .setDpDesc("Datapath Description")
-                .setHwDesc("Hardware Description")
-                .setMfrDesc("Manufacturer Description")
-                .setSwDesc("Software Description")
-                .setSerialNum("Serial Number")
-                .build();
-        return statsReply;
-    }
-    
-    protected OFTableFeaturesStatsReply createTableFeaturesStatsReply() {
-    	OFTableFeaturesStatsReply statsReply = factory.buildTableFeaturesStatsReply()
-    			.setEntries(Collections.singletonList(factory.buildTableFeatures()
-    					.setConfig(0)
-    					.setMaxEntries(100)
-    					.setMetadataMatch(U64.NO_MASK)
-    					.setMetadataWrite(U64.NO_MASK)
-    					.setName("MyTable")
-    					.setTableId(TableId.of(1))
-    					.setProperties(Collections.singletonList((OFTableFeatureProp)factory.buildTableFeaturePropMatch()
-    							.setOxmIds(Collections.singletonList(U32.of(100)))
-    							.build())
-    				).build()
-    			)
-    			
-    		).build();
-    	return statsReply;
-    }
-
-    /**
-     * setup the expectations for the mock switch that are needed
-     * after the switch is instantiated in the WAIT_DESCRIPTION_STATS STATE
-     * Will reset the switch
-     * @throws CounterException
-     */
-    protected void setupSwitchForInstantiationWithReset()
-            throws Exception {
-        reset(sw);
-        sw.setFeaturesReply(featuresReply);
-        expectLastCall().once();
-    }
-
-    /**
-     * Tests a situation where a switch returns a QUARANTINE result. This means
-     * we should move the handshake handler to a quarantine state and also
-     * quarantine the switch in the controller.
-     *
-     * @throws Exception
-     */
-    @Test
-    public void moveQuarantine() throws Exception {
-        moveToWaitAppHandshakeState();
-
-        reset(switchManager);
-        switchManager.switchStatusChanged(sw, SwitchStatus.HANDSHAKE, SwitchStatus.QUARANTINED);
-        expectLastCall().once();
-        replay(switchManager);
-
-        assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(WaitAppHandshakeState.class));
-        WaitAppHandshakeState state = (WaitAppHandshakeState) switchHandler.getStateForTesting();
-        assertThat(state.getCurrentPlugin(), CoreMatchers.<OFSwitchAppHandshakePlugin>equalTo(handshakePlugin));
-
-        reset(sw);
-        expect(sw.getStatus()).andReturn(SwitchStatus.HANDSHAKE);
-        sw.setStatus(SwitchStatus.QUARANTINED);
-        expectLastCall().once();
-        replay(sw);
-
-        PluginResult result = new PluginResult(PluginResultType.QUARANTINE, "test quarantine");
-        handshakePlugin.exitPlugin(result);
-
-        assertThat(switchHandler.getStateForTesting(),
-                   CoreMatchers.instanceOf(QuarantineState.class));
-        verify(switchManager);
-    }
-
-    /**
-     * Tests a situation where a plugin returns a DISCONNECT result. This means
-     * we should disconnect the connection and the state should not change.
-     *
-     * @throws Exception
-     */
-    @Test
-    public void failedAppHandshake() throws Exception {
-        moveToWaitAppHandshakeState();
-
-        assertThat(switchHandler.getStateForTesting(),
-                   CoreMatchers.instanceOf(WaitAppHandshakeState.class));
-
-        WaitAppHandshakeState state = (WaitAppHandshakeState) switchHandler.getStateForTesting();
-        assertThat(state.getCurrentPlugin(), CoreMatchers.<OFSwitchAppHandshakePlugin>equalTo(handshakePlugin));
-
-        PluginResult result = new PluginResult(PluginResultType.DISCONNECT);
-        handshakePlugin.exitPlugin(result);
-
-        assertThat(connection.isConnected(), equalTo(false));
-    }
-
-
-    @Test
-    public void validAppHandshakePluginReason() throws Exception {
-        try{
-            new PluginResult(PluginResultType.QUARANTINE,"This should not cause an exception");
-        }catch(IllegalStateException e) {
-            fail("This should cause an illegal state exception");
-        }
-    }
-
-    @Test
-    public void invalidAppHandshakePluginReason() throws Exception {
-        try{
-            new PluginResult(PluginResultType.CONTINUE,"This should cause an exception");
-            fail("This should cause an illegal state exception");
-        }catch(IllegalStateException e) { /* Expected */ }
-
-        try{
-            new PluginResult(PluginResultType.DISCONNECT,"This should cause an exception");
-            fail("This should cause an illegal state exception");
-        }catch(IllegalStateException e) { /* Expected */ }
-    }
-
-    /**
-     * Move the channel from scratch to WAIT_INITIAL_ROLE state via
-     * WAIT_SWITCH_DRIVER_SUB_HANDSHAKE
-     * Does extensive testing for the WAIT_SWITCH_DRIVER_SUB_HANDSHAKE state
-     *
-     */
-    @Test
-    public void testSwitchDriverSubHandshake()
-            throws Exception {
-        moveToWaitSwitchDriverSubHandshake();
-
-        //-------------------------------------------------
-        //-------------------------------------------------
-        // Send a message to the handler, it should be passed to the
-        // switch's sub-handshake handling. After this message the
-        // sub-handshake will be complete
-        // FIXME:LOJI: With Andi's fix for a default Match object we won't
-        // need to build/set this match object
-
-        Match match = factory.buildMatch().build();
-        OFMessage m = factory.buildFlowRemoved().setMatch(match).build();
-        resetToStrict(sw);
-        sw.processDriverHandshakeMessage(m);
-        expectLastCall().once();
-        expect(sw.isDriverHandshakeComplete()).andReturn(true).once();
-        replay(sw);
-
-        switchHandler.processOFMessage(m);
-
-        assertThat(switchHandler.getStateForTesting(),
-                   CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitAppHandshakeState.class));
-        assertThat("Unexpected message captured", connection.getMessages(), Matchers.empty());
-        verify(sw);
-    }
-
-    @Test
-    /** Test WaitDescriptionReplyState */
-    public void testWaitDescriptionReplyState() throws Exception {
-        moveToWaitInitialRole();
-    }
-
-    /**
-     * Setup the mock switch and write capture for a role request, set the
-     * role and verify mocks.
-     * @param supportsNxRole whether the switch supports role request messages
-     * to setup the attribute. This must be null (don't yet know if roles
-     * supported: send to check) or true.
-     * @param role The role to send
-     * @throws IOException
-     */
-    private long setupSwitchSendRoleRequestAndVerify(Boolean supportsNxRole,
-                                           OFControllerRole role) throws IOException {
-        assertTrue("This internal test helper method most not be called " +
-                   "with supportsNxRole==false. Test setup broken",
-                   supportsNxRole == null || supportsNxRole == true);
-        reset(sw);
-        expect(sw.getAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE))
-                .andReturn(supportsNxRole).atLeastOnce();
-        replay(sw);
-
-        switchHandler.sendRoleRequest(role);
-
-        OFMessage msg = connection.retrieveMessage();
-        verifyRoleRequest(msg, role);
-        verify(sw);
-        return msg.getXid();
-    }
-
-
-    /**
-     * Setup the mock switch for a role change request where the switch
-     * does not support roles.
-     *
-     * Needs to verify and reset the controller since we need to set
-     * an expectation
-     */
-    @SuppressWarnings("unchecked")
+	 */
+	@Test
+	public void testInitState() throws Exception {
+		assertThat(connection.getListener(), notNullValue());
+		assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.InitState.class));
+	}
+
+
+	/** Move the channel from scratch to WAIT_CONFIG_REPLY state
+	 * adds testing for beginHandshake() which moves the state from
+	 * InitState to WaitConfigReply.
+	 */
+	@Test
+	public void moveToWaitConfigReply() throws Exception {
+		moveToPreConfigReply();
+
+		List<OFMessage> msgs = connection.getMessages();
+		assertEquals(3, msgs.size());
+		assertEquals(OFType.SET_CONFIG, msgs.get(0).getType());
+		OFSetConfig sc = (OFSetConfig)msgs.get(0);
+		assertEquals(0xffff, sc.getMissSendLen());
+		assertEquals(OFType.BARRIER_REQUEST, msgs.get(1).getType());
+		assertEquals(OFType.GET_CONFIG_REQUEST, msgs.get(2).getType());
+		verifyUniqueXids(msgs);
+		msgs.clear();
+		assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitConfigReplyState.class));
+		verifyAll();
+	}
+
+
+
+	/** Move the channel from scratch to WAIT_DESCRIPTION_STAT_REPLY state
+	 * Builds on moveToWaitConfigReply()
+	 * adds testing for WAIT_CONFIG_REPLY state
+	 */
+	@Test
+	public void moveToWaitDescriptionStatReply() throws Exception {
+		moveToWaitConfigReply();
+
+		connection.clearMessages();
+		OFGetConfigReply cr = factory.buildGetConfigReply()
+				.setMissSendLen(0xFFFF)
+				.build();
+
+		switchHandler.processOFMessage(cr);
+
+		OFMessage msg = connection.retrieveMessage();
+		assertEquals(OFType.STATS_REQUEST, msg.getType());
+		OFStatsRequest<?> sr = (OFStatsRequest<?>)msg;
+		assertEquals(OFStatsType.DESC, sr.getStatsType());
+		verifyUniqueXids(msg);
+		assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitDescriptionStatReplyState.class));
+	}
+
+	protected OFDescStatsReply createDescriptionStatsReply() {
+		OFDescStatsReply statsReply = factory.buildDescStatsReply()
+				.setDpDesc("Datapath Description")
+				.setHwDesc("Hardware Description")
+				.setMfrDesc("Manufacturer Description")
+				.setSwDesc("Software Description")
+				.setSerialNum("Serial Number")
+				.build();
+		return statsReply;
+	}
+
+	protected OFTableFeaturesStatsReply createTableFeaturesStatsReply() {
+		OFTableFeaturesStatsReply statsReply = factory.buildTableFeaturesStatsReply()
+				.setEntries(Collections.singletonList(factory.buildTableFeatures()
+						.setConfig(0)
+						.setMaxEntries(100)
+						.setMetadataMatch(U64.NO_MASK)
+						.setMetadataWrite(U64.NO_MASK)
+						.setName("MyTable")
+						.setTableId(TableId.of(1))
+						.setProperties(Collections.singletonList((OFTableFeatureProp)factory.buildTableFeaturePropMatch()
+								.setOxmIds(Collections.singletonList(U32.of(100)))
+								.build())
+								).build()
+						)
+
+						).build();
+		return statsReply;
+	}
+
+	/**
+	 * setup the expectations for the mock switch that are needed
+	 * after the switch is instantiated in the WAIT_DESCRIPTION_STATS STATE
+	 * Will reset the switch
+	 * @throws CounterException
+	 */
+	protected void setupSwitchForInstantiationWithReset()
+			throws Exception {
+		reset(sw);
+		sw.setFeaturesReply(featuresReply);
+		expectLastCall().once();
+	}
+
+	/**
+	 * Tests a situation where a switch returns a QUARANTINE result. This means
+	 * we should move the handshake handler to a quarantine state and also
+	 * quarantine the switch in the controller.
+	 *
+	 * @throws Exception
+	 */
+	@Test
+	public void moveQuarantine() throws Exception {
+		moveToWaitAppHandshakeState();
+
+		reset(switchManager);
+		switchManager.switchStatusChanged(sw, SwitchStatus.HANDSHAKE, SwitchStatus.QUARANTINED);
+		expectLastCall().once();
+		replay(switchManager);
+
+		assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(WaitAppHandshakeState.class));
+		WaitAppHandshakeState state = (WaitAppHandshakeState) switchHandler.getStateForTesting();
+		assertThat(state.getCurrentPlugin(), CoreMatchers.<OFSwitchAppHandshakePlugin>equalTo(handshakePlugin));
+
+		reset(sw);
+		expect(sw.getStatus()).andReturn(SwitchStatus.HANDSHAKE);
+		sw.setStatus(SwitchStatus.QUARANTINED);
+		expectLastCall().once();
+		replay(sw);
+
+		PluginResult result = new PluginResult(PluginResultType.QUARANTINE, "test quarantine");
+		handshakePlugin.exitPlugin(result);
+
+		assertThat(switchHandler.getStateForTesting(),
+				CoreMatchers.instanceOf(QuarantineState.class));
+		verify(switchManager);
+	}
+
+	/**
+	 * Tests a situation where a plugin returns a DISCONNECT result. This means
+	 * we should disconnect the connection and the state should not change.
+	 *
+	 * @throws Exception
+	 */
+	@Test
+	public void failedAppHandshake() throws Exception {
+		moveToWaitAppHandshakeState();
+
+		assertThat(switchHandler.getStateForTesting(),
+				CoreMatchers.instanceOf(WaitAppHandshakeState.class));
+
+		WaitAppHandshakeState state = (WaitAppHandshakeState) switchHandler.getStateForTesting();
+		assertThat(state.getCurrentPlugin(), CoreMatchers.<OFSwitchAppHandshakePlugin>equalTo(handshakePlugin));
+
+		PluginResult result = new PluginResult(PluginResultType.DISCONNECT);
+		handshakePlugin.exitPlugin(result);
+
+		assertThat(connection.isConnected(), equalTo(false));
+	}
+
+
+	@Test
+	public void validAppHandshakePluginReason() throws Exception {
+		try{
+			new PluginResult(PluginResultType.QUARANTINE,"This should not cause an exception");
+		}catch(IllegalStateException e) {
+			fail("This should cause an illegal state exception");
+		}
+	}
+
+	@Test
+	public void invalidAppHandshakePluginReason() throws Exception {
+		try{
+			new PluginResult(PluginResultType.CONTINUE,"This should cause an exception");
+			fail("This should cause an illegal state exception");
+		}catch(IllegalStateException e) { /* Expected */ }
+
+		try{
+			new PluginResult(PluginResultType.DISCONNECT,"This should cause an exception");
+			fail("This should cause an illegal state exception");
+		}catch(IllegalStateException e) { /* Expected */ }
+	}
+
+	/**
+	 * Move the channel from scratch to WAIT_INITIAL_ROLE state via
+	 * WAIT_SWITCH_DRIVER_SUB_HANDSHAKE
+	 * Does extensive testing for the WAIT_SWITCH_DRIVER_SUB_HANDSHAKE state
+	 *
+	 */
+	@Test
+	public void testSwitchDriverSubHandshake()
+			throws Exception {
+		moveToWaitSwitchDriverSubHandshake();
+
+		//-------------------------------------------------
+		//-------------------------------------------------
+		// Send a message to the handler, it should be passed to the
+		// switch's sub-handshake handling. After this message the
+		// sub-handshake will be complete
+		// FIXME:LOJI: With Andi's fix for a default Match object we won't
+		// need to build/set this match object
+
+		Match match = factory.buildMatch().build();
+		OFMessage m = factory.buildFlowRemoved().setMatch(match).build();
+		resetToStrict(sw);
+		sw.processDriverHandshakeMessage(m);
+		expectLastCall().once();
+		expect(sw.isDriverHandshakeComplete()).andReturn(true).once();
+		replay(sw);
+
+		switchHandler.processOFMessage(m);
+
+		assertThat(switchHandler.getStateForTesting(),
+				CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitAppHandshakeState.class));
+		assertThat("Unexpected message captured", connection.getMessages(), Matchers.empty());
+		verify(sw);
+	}
+
+	@Test
+	/** Test WaitDescriptionReplyState */
+	public void testWaitDescriptionReplyState() throws Exception {
+		moveToWaitInitialRole();
+	}
+
+	/**
+	 * Setup the mock switch and write capture for a role request, set the
+	 * role and verify mocks.
+	 * @param supportsNxRole whether the switch supports role request messages
+	 * to setup the attribute. This must be null (don't yet know if roles
+	 * supported: send to check) or true.
+	 * @param role The role to send
+	 * @throws IOException
+	 */
+	private long setupSwitchSendRoleRequestAndVerify(Boolean supportsNxRole,
+			OFControllerRole role) throws IOException {
+		assertTrue("This internal test helper method most not be called " +
+				"with supportsNxRole==false. Test setup broken",
+				supportsNxRole == null || supportsNxRole == true);
+		reset(sw);
+		expect(sw.getAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE))
+		.andReturn(supportsNxRole).atLeastOnce();
+		replay(sw);
+
+		switchHandler.sendRoleRequest(role);
+
+		OFMessage msg = connection.retrieveMessage();
+		verifyRoleRequest(msg, role);
+		verify(sw);
+		return msg.getXid();
+	}
+
+
+	/**
+	 * Setup the mock switch for a role change request where the switch
+	 * does not support roles.
+	 *
+	 * Needs to verify and reset the controller since we need to set
+	 * an expectation
+	 */
+	@SuppressWarnings("unchecked")
 	private void setupSwitchRoleChangeUnsupported(int xid,
-                                                  OFControllerRole role) {
-        SwitchStatus newStatus = role != OFControllerRole.ROLE_SLAVE ? SwitchStatus.MASTER : SwitchStatus.SLAVE;
-        boolean supportsNxRole = false;
-        verify(switchManager);
-        reset(sw, switchManager);
-        expect(sw.getAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE))
-                .andReturn(supportsNxRole).atLeastOnce();
-        // TODO: hmmm. While it's not incorrect that we set the attribute
-        // again it looks odd. Maybe change
-        expect(sw.getOFFactory()).andReturn(factory).anyTimes();
-        sw.write(anyObject(OFMessage.class));
-        expectLastCall().anyTimes();
-        sw.write(anyObject(Iterable.class));
-        expectLastCall().anyTimes();
-        expect(sw.getTables()).andStubReturn((short)0);
-        sw.setAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, supportsNxRole);
-        expectLastCall().anyTimes();
-        sw.setControllerRole(role);
-        expectLastCall().once();
-
-        if (role == OFControllerRole.ROLE_SLAVE) {
-            sw.disconnect();
-            expectLastCall().once();
-        } else {
-            expect(sw.getStatus()).andReturn(SwitchStatus.HANDSHAKE).once();
-            sw.setStatus(newStatus);
-            expectLastCall().once();
-            switchManager.switchStatusChanged(sw, SwitchStatus.HANDSHAKE, newStatus);
-        }
-        replay(sw, switchManager);
-
-        switchHandler.sendRoleRequest(role);
-
-        verify(sw, switchManager);
-    }
-
-    /** Return a bad request error message with the given xid/code */
-    private OFMessage getBadRequestErrorMessage(OFBadRequestCode code, long xid) {
-        OFErrorMsg msg = factory.errorMsgs().buildBadRequestErrorMsg()
-                .setXid(xid)
-                .setCode(code)
-                .build();
-        return msg;
-    }
-
-    /** Return a bad action error message with the given xid/code */
-    private OFMessage getBadActionErrorMessage(OFBadActionCode code, long xid) {
-        OFErrorMsg msg = factory.errorMsgs().buildBadActionErrorMsg()
-                .setXid(xid)
-                .setCode(code)
-                .build();
-        return msg;
-    }
-
-
-    /** Move the channel from scratch to MASTER state
-     * Builds on doMoveToWaitInitialRole()
-     * adds testing for WAIT_INITAL_ROLE state
-     *
-     * This method tests only the simple case that the switch supports roles
-     * and transitions to MASTER
-     */
-    @SuppressWarnings("unchecked")
+			OFControllerRole role) {
+		SwitchStatus newStatus = role != OFControllerRole.ROLE_SLAVE ? SwitchStatus.MASTER : SwitchStatus.SLAVE;
+		boolean supportsNxRole = false;
+		verify(switchManager);
+		reset(sw, switchManager);
+		expect(sw.getAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE))
+		.andReturn(supportsNxRole).atLeastOnce();
+		// TODO: hmmm. While it's not incorrect that we set the attribute
+		// again it looks odd. Maybe change
+		expect(sw.getOFFactory()).andReturn(factory).anyTimes();
+		sw.write(anyObject(OFMessage.class));
+		expectLastCall().anyTimes();
+		sw.write(anyObject(Iterable.class));
+		expectLastCall().anyTimes();
+		expect(sw.getNumTables()).andStubReturn((short)0);
+		sw.setAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, supportsNxRole);
+		expectLastCall().anyTimes();
+		if (SwitchStatus.MASTER == newStatus) {
+			if (factory.getVersion().compareTo(OFVersion.OF_13) >= 0) {
+				expect(sw.getTables()).andReturn(Collections.EMPTY_LIST).once();
+				expect(sw.getTableFeatures(TableId.ZERO)).andReturn(TableFeatures.of(createTableFeaturesStatsReply().getEntries().get(0))).anyTimes();
+			}
+		}
+
+		sw.setControllerRole(role);
+		expectLastCall().once();
+
+		if (role == OFControllerRole.ROLE_SLAVE) {
+			sw.disconnect();
+			expectLastCall().once();
+		} else {
+			expect(sw.getStatus()).andReturn(SwitchStatus.HANDSHAKE).once();
+			sw.setStatus(newStatus);
+			expectLastCall().once();
+			switchManager.switchStatusChanged(sw, SwitchStatus.HANDSHAKE, newStatus);
+		}
+		replay(sw, switchManager);
+		
+		switchHandler.sendRoleRequest(role);
+		
+		/* Now, trigger transition to master */
+		OFBarrierReply br = getFactory().buildBarrierReply()
+				.build();
+		switchHandler.processOFMessage(br);
+
+		verify(sw, switchManager);
+	}
+
+	/** Return a bad request error message with the given xid/code */
+	private OFMessage getBadRequestErrorMessage(OFBadRequestCode code, long xid) {
+		OFErrorMsg msg = factory.errorMsgs().buildBadRequestErrorMsg()
+				.setXid(xid)
+				.setCode(code)
+				.build();
+		return msg;
+	}
+
+	/** Return a bad action error message with the given xid/code */
+	private OFMessage getBadActionErrorMessage(OFBadActionCode code, long xid) {
+		OFErrorMsg msg = factory.errorMsgs().buildBadActionErrorMsg()
+				.setXid(xid)
+				.setCode(code)
+				.build();
+		return msg;
+	}
+
+
+	/** Move the channel from scratch to MASTER state
+	 * Builds on doMoveToWaitInitialRole()
+	 * adds testing for WAIT_INITAL_ROLE state
+	 *
+	 * This method tests only the simple case that the switch supports roles
+	 * and transitions to MASTER
+	 */
+	@SuppressWarnings("unchecked")
+	@Test
+	public void testInitialMoveToMasterWithRole() throws Exception {
+		// first, move us to WAIT_INITIAL_ROLE_STATE
+		moveToWaitInitialRole();
+		assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class));
+
+		// Set the role
+		long xid = setupSwitchSendRoleRequestAndVerify(null, OFControllerRole.ROLE_MASTER);
+		assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class));
+
+		// prepare mocks and inject the role reply message
+		reset(sw);
+		expect(sw.getOFFactory()).andReturn(factory).anyTimes();
+		sw.write(anyObject(OFMessage.class));
+		expectLastCall().anyTimes();
+		sw.write(anyObject(Iterable.class));
+		expectLastCall().anyTimes();
+		expect(sw.getTables()).andStubReturn(Collections.EMPTY_LIST);
+		expect(sw.getNumTables()).andStubReturn((short) 0);
+		sw.setAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, true);
+		expectLastCall().once();
+		sw.setControllerRole(OFControllerRole.ROLE_MASTER);
+		expectLastCall().once();
+		expect(sw.getStatus()).andReturn(SwitchStatus.HANDSHAKE).once();
+		sw.setStatus(SwitchStatus.MASTER);
+		expectLastCall().once();
+
+		if (factory.getVersion().compareTo(OFVersion.OF_13) >= 0) {
+			//expect(sw.getMaxTableForTableMissFlow()).andReturn(TableId.ZERO).times(1);
+			expect(sw.getTableFeatures(TableId.ZERO)).andReturn(TableFeatures.of(createTableFeaturesStatsReply().getEntries().get(0))).anyTimes();
+		}
+		replay(sw);
+
+		reset(switchManager);
+		switchManager.switchStatusChanged(sw, SwitchStatus.HANDSHAKE, SwitchStatus.MASTER);
+		expectLastCall().once();
+		replay(switchManager);
+		OFMessage reply = getRoleReply(xid, OFControllerRole.ROLE_MASTER);
+
+		/* Go into the MasterState */
+		switchHandler.processOFMessage(reply);
+		
+		assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.MasterState.class));
+	}
+
+	/** Move the channel from scratch to SLAVE state
+	 * Builds on doMoveToWaitInitialRole()
+	 * adds testing for WAIT_INITAL_ROLE state
+	 *
+	 * This method tests only the simple case that the switch supports roles
+	 * and transitions to SLAVE
+	 */
+	@Test
+	public void testInitialMoveToSlaveWithRole() throws Exception {
+
+		// first, move us to WAIT_INITIAL_ROLE_STATE
+		moveToWaitInitialRole();
+		assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class));
+
+		// Set the role
+		long xid = setupSwitchSendRoleRequestAndVerify(null, OFControllerRole.ROLE_SLAVE);
+		assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class));
+
+		// prepare mocks and inject the role reply message
+		reset(sw);
+		sw.setAttribute(IOFSwitchBackend.SWITCH_SUPPORTS_NX_ROLE, true);
+		expectLastCall().once();
+		sw.setControllerRole(OFControllerRole.ROLE_SLAVE);
+		expectLastCall().once();
+		expect(sw.getStatus()).andReturn(SwitchStatus.HANDSHAKE).once();
+		sw.setStatus(SwitchStatus.SLAVE);
+		expectLastCall().once();
+		replay(sw);
+
+		reset(switchManager);
+		switchManager.switchStatusChanged(sw, SwitchStatus.HANDSHAKE, SwitchStatus.SLAVE);
+		expectLastCall().once();
+		replay(switchManager);
+
+		OFMessage reply = getRoleReply(xid, OFControllerRole.ROLE_SLAVE);
+
+		// sendMessageToHandler will verify and rest controller mock
+		switchHandler.processOFMessage(reply);
+
+		assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.SlaveState.class));
+	}
+
+	/** Move the channel from scratch to MASTER state
+	 * Builds on doMoveToWaitInitialRole()
+	 * adds testing for WAIT_INITAL_ROLE state
+	 *
+	 * This method tests the case that the switch does NOT support roles.
+	 * The channel handler still needs to send the initial request to find
+	 * out that whether the switch supports roles.
+	 */
+	@SuppressWarnings("unchecked")
+	@Test
+	public void testInitialMoveToMasterNoRole() throws Exception {
+		// first, move us to WAIT_INITIAL_ROLE_STATE
+		moveToWaitInitialRole();
+		assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class));
+
+		// Set the role
+		long xid = setupSwitchSendRoleRequestAndVerify(null, OFControllerRole.ROLE_MASTER);
+		assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class));
+
+		// prepare mocks and inject the role reply message
+		reset(sw);
+		expect(sw.getOFFactory()).andReturn(factory).anyTimes();
+		sw.write(anyObject(OFMessage.class));
+		expectLastCall().anyTimes();
+		sw.write(anyObject(Iterable.class));
+		expectLastCall().anyTimes();
+		expect(sw.getNumTables()).andStubReturn((short)0);
+		sw.setAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, false);
+		expectLastCall().once();
+		sw.setControllerRole(OFControllerRole.ROLE_MASTER);
+		expectLastCall().once();
+		expect(sw.getStatus()).andReturn(SwitchStatus.HANDSHAKE).once();
+		sw.setStatus(SwitchStatus.MASTER);
+		expectLastCall().once();
+		if (factory.getVersion().compareTo(OFVersion.OF_13) >= 0) {
+			expect(sw.getTables()).andReturn(Collections.EMPTY_LIST).once();
+			expect(sw.getTableFeatures(TableId.ZERO)).andReturn(TableFeatures.of(createTableFeaturesStatsReply().getEntries().get(0))).anyTimes();
+		}
+		replay(sw);
+
+		// FIXME: shouldn't use ordinal(), but OFError is broken
+
+		// Error with incorrect xid and type. Should be ignored.
+		OFMessage err = getBadActionErrorMessage(OFBadActionCode.BAD_TYPE, xid+1);
+		// sendMessageToHandler will verify and rest controller mock
+		switchHandler.processOFMessage(err);
+		assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class));
+
+		// Error with correct xid. Should trigger state transition
+		err = getBadRequestErrorMessage(OFBadRequestCode.BAD_EXPERIMENTER, xid);
+		reset(switchManager);
+		switchManager.switchStatusChanged(sw, SwitchStatus.HANDSHAKE, SwitchStatus.MASTER);
+		expectLastCall().once();
+		replay(switchManager);
+		
+		/* Go into the MasterState */
+		switchHandler.processOFMessage(err);
+
+		assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.MasterState.class));
+	}
+
+	/** Move the channel from scratch to MASTER state
+	 * Builds on doMoveToWaitInitialRole()
+	 * adds testing for WAIT_INITAL_ROLE state
+	 *
+	 * We let the initial role request time out. Role support should be
+	 * disabled but the switch should be activated.
+	 */
+	@SuppressWarnings("unchecked")
+	@Test
+	public void testInitialMoveToMasterTimeout() throws Exception {
+		int timeout = 50;
+		switchHandler.useRoleChangerWithOtherTimeoutForTesting(timeout);
+
+		// first, move us to WAIT_INITIAL_ROLE_STATE
+		moveToWaitInitialRole();
+		assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class));
+
+		// Set the role
+		setupSwitchSendRoleRequestAndVerify(null, OFControllerRole.ROLE_MASTER); /* don't care about the XID, since we assume the reply is lost */
+		assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class));
+
+		// prepare mocks and inject the role reply message
+		reset(sw);
+		expect(sw.getOFFactory()).andReturn(factory).anyTimes();
+		sw.write(anyObject(OFMessage.class));
+		expectLastCall().anyTimes();
+		sw.write(anyObject(Iterable.class));
+		expectLastCall().anyTimes();
+		expect(sw.getNumTables()).andStubReturn((short)0);
+		sw.setAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, false);
+		expectLastCall().once();
+		sw.setControllerRole(OFControllerRole.ROLE_MASTER);
+		expectLastCall().once();
+		expect(sw.getStatus()).andReturn(SwitchStatus.HANDSHAKE).once();
+		sw.setStatus(SwitchStatus.MASTER);
+		expectLastCall().once();
+		if (factory.getVersion().compareTo(OFVersion.OF_13) >= 0) {
+			expect(sw.getTables()).andReturn(Collections.EMPTY_LIST).once();
+			expect(sw.getTableFeatures(TableId.ZERO)).andReturn(TableFeatures.of(createTableFeaturesStatsReply().getEntries().get(0))).anyTimes();
+		}
+		replay(sw);
+
+		OFMessage m = factory.barrierReply();
+
+		Thread.sleep(timeout + 5);
+
+		reset(switchManager);
+		switchManager.switchStatusChanged(sw, SwitchStatus.HANDSHAKE, SwitchStatus.MASTER);
+		expectLastCall().once();
+		replay(switchManager);
+		switchHandler.processOFMessage(m);
+
+		assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.MasterState.class));
+
+	}
+
+
+	/** Move the channel from scratch to SLAVE state
+	 * Builds on doMoveToWaitInitialRole()
+	 * adds testing for WAIT_INITAL_ROLE state
+	 *
+	 * This method tests the case that the switch does NOT support roles.
+	 * The channel handler still needs to send the initial request to find
+	 * out that whether the switch supports roles.
+	 *
+	 */
 	@Test
-    public void testInitialMoveToMasterWithRole() throws Exception {
-        // first, move us to WAIT_INITIAL_ROLE_STATE
-        moveToWaitInitialRole();
-        assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class));
-
-        // Set the role
-        long xid = setupSwitchSendRoleRequestAndVerify(null, OFControllerRole.ROLE_MASTER);
-        assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class));
-
-        // prepare mocks and inject the role reply message
-        reset(sw);
-        expect(sw.getOFFactory()).andReturn(factory).anyTimes();
-        sw.write(anyObject(OFMessage.class));
-        expectLastCall().anyTimes();
-        sw.write(anyObject(Iterable.class));
-        expectLastCall().anyTimes();
-        expect(sw.getTables()).andStubReturn((short)0);
-        sw.setAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, true);
-        expectLastCall().once();
-        sw.setControllerRole(OFControllerRole.ROLE_MASTER);
-        expectLastCall().once();
-        expect(sw.getStatus()).andReturn(SwitchStatus.HANDSHAKE).once();
-        sw.setStatus(SwitchStatus.MASTER);
-        expectLastCall().once();
-        replay(sw);
-
-        reset(switchManager);
-        switchManager.switchStatusChanged(sw, SwitchStatus.HANDSHAKE, SwitchStatus.MASTER);
-        expectLastCall().once();
-        replay(switchManager);
-        OFMessage reply = getRoleReply(xid, OFControllerRole.ROLE_MASTER);
-
-        // sendMessageToHandler will verify and rest controller mock
-        switchHandler.processOFMessage(reply);
-
-        assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.MasterState.class));
-    }
-
-    /** Move the channel from scratch to SLAVE state
-     * Builds on doMoveToWaitInitialRole()
-     * adds testing for WAIT_INITAL_ROLE state
-     *
-     * This method tests only the simple case that the switch supports roles
-     * and transitions to SLAVE
-     */
-    @Test
-    public void testInitialMoveToSlaveWithRole() throws Exception {
-
-        // first, move us to WAIT_INITIAL_ROLE_STATE
-        moveToWaitInitialRole();
-        assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class));
-
-        // Set the role
-        long xid = setupSwitchSendRoleRequestAndVerify(null, OFControllerRole.ROLE_SLAVE);
-        assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class));
-
-        // prepare mocks and inject the role reply message
-        reset(sw);
-        sw.setAttribute(IOFSwitchBackend.SWITCH_SUPPORTS_NX_ROLE, true);
-        expectLastCall().once();
-        sw.setControllerRole(OFControllerRole.ROLE_SLAVE);
-        expectLastCall().once();
-        expect(sw.getStatus()).andReturn(SwitchStatus.HANDSHAKE).once();
-        sw.setStatus(SwitchStatus.SLAVE);
-        expectLastCall().once();
-        replay(sw);
-
-        reset(switchManager);
-        switchManager.switchStatusChanged(sw, SwitchStatus.HANDSHAKE, SwitchStatus.SLAVE);
-        expectLastCall().once();
-        replay(switchManager);
-
-        OFMessage reply = getRoleReply(xid, OFControllerRole.ROLE_SLAVE);
-
-        // sendMessageToHandler will verify and rest controller mock
-        switchHandler.processOFMessage(reply);
-
-        assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.SlaveState.class));
-    }
-
-    /** Move the channel from scratch to MASTER state
-     * Builds on doMoveToWaitInitialRole()
-     * adds testing for WAIT_INITAL_ROLE state
-     *
-     * This method tests the case that the switch does NOT support roles.
-     * The channel handler still needs to send the initial request to find
-     * out that whether the switch supports roles.
-     */
-    @SuppressWarnings("unchecked")
+	public void testInitialMoveToSlaveNoRole() throws Exception {
+		// first, move us to WAIT_INITIAL_ROLE_STATE
+		moveToWaitInitialRole();
+		assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class));
+
+		// Set the role
+		long xid = setupSwitchSendRoleRequestAndVerify(null, OFControllerRole.ROLE_SLAVE);
+		assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class));
+
+		// prepare mocks and inject the role reply message
+		reset(sw);
+		sw.setAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, false);
+		expectLastCall().once();
+		sw.setControllerRole(OFControllerRole.ROLE_SLAVE);
+		expectLastCall().once();
+		sw.disconnect(); // Make sure we disconnect
+		expectLastCall().once();
+		replay(sw);
+
+
+		// FIXME: shouldn't use ordinal(), but OFError is broken
+
+		// Error with incorrect xid and type. Should be ignored.
+		OFMessage err = getBadActionErrorMessage(OFBadActionCode.BAD_TYPE, xid+1);
+
+		// sendMessageToHandler will verify and rest controller mock
+		switchHandler.processOFMessage(err);
+		assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class));
+
+		// Error with correct xid. Should trigger state transition
+		err = getBadRequestErrorMessage(OFBadRequestCode.BAD_EXPERIMENTER, xid);
+		// sendMessageToHandler will verify and rest controller mock
+		switchHandler.processOFMessage(err);
+	}
+
+
+	/** Move the channel from scratch to SLAVE state
+	 * Builds on doMoveToWaitInitialRole()
+	 * adds testing for WAIT_INITAL_ROLE state
+	 *
+	 * We let the initial role request time out. The switch should be
+	 * disconnected
+	 */
 	@Test
-    public void testInitialMoveToMasterNoRole() throws Exception {
-        // first, move us to WAIT_INITIAL_ROLE_STATE
-        moveToWaitInitialRole();
-        assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class));
-
-        // Set the role
-        long xid = setupSwitchSendRoleRequestAndVerify(null, OFControllerRole.ROLE_MASTER);
-        assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class));
-
-        // prepare mocks and inject the role reply message
-        reset(sw);
-        expect(sw.getOFFactory()).andReturn(factory).anyTimes();
-        sw.write(anyObject(OFMessage.class));
-        expectLastCall().anyTimes();
-        sw.write(anyObject(Iterable.class));
-        expectLastCall().anyTimes();
-        expect(sw.getTables()).andStubReturn((short)0);
-        sw.setAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, false);
-        expectLastCall().once();
-        sw.setControllerRole(OFControllerRole.ROLE_MASTER);
-        expectLastCall().once();
-        expect(sw.getStatus()).andReturn(SwitchStatus.HANDSHAKE).once();
-        sw.setStatus(SwitchStatus.MASTER);
-        expectLastCall().once();
-        replay(sw);
-
-        // FIXME: shouldn't use ordinal(), but OFError is broken
-
-        // Error with incorrect xid and type. Should be ignored.
-        OFMessage err = getBadActionErrorMessage(OFBadActionCode.BAD_TYPE, xid+1);
-        // sendMessageToHandler will verify and rest controller mock
-        switchHandler.processOFMessage(err);
-        assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class));
-
-        // Error with correct xid. Should trigger state transition
-        err = getBadRequestErrorMessage(OFBadRequestCode.BAD_EXPERIMENTER, xid);
-        reset(switchManager);
-        switchManager.switchStatusChanged(sw, SwitchStatus.HANDSHAKE, SwitchStatus.MASTER);
-        expectLastCall().once();
-        replay(switchManager);
-        // sendMessageToHandler will verify and rest controller mock
-        switchHandler.processOFMessage(err);
-
-        assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.MasterState.class));
-    }
-
-    /** Move the channel from scratch to MASTER state
-     * Builds on doMoveToWaitInitialRole()
-     * adds testing for WAIT_INITAL_ROLE state
-     *
-     * We let the initial role request time out. Role support should be
-     * disabled but the switch should be activated.
-     */
-    @SuppressWarnings("unchecked")
+	public void testInitialMoveToSlaveTimeout() throws Exception {
+		int timeout = 50;
+		switchHandler.useRoleChangerWithOtherTimeoutForTesting(timeout);
+
+		// first, move us to WAIT_INITIAL_ROLE_STATE
+		moveToWaitInitialRole();
+		assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class));
+
+		// Set the role
+		setupSwitchSendRoleRequestAndVerify(null, OFControllerRole.ROLE_SLAVE);
+		assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class));
+
+		// prepare mocks and inject the role reply message
+		reset(sw);
+		sw.setAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, false);
+		expectLastCall().once();
+		sw.setControllerRole(OFControllerRole.ROLE_SLAVE);
+		expectLastCall().once();
+		sw.disconnect(); // Make sure we disconnect
+		expectLastCall().once();
+		replay(sw);
+
+		// Apparently this can be any type of message for this test?!
+		OFMessage m = factory.buildBarrierReply().build();
+
+		Thread.sleep(timeout+5);
+		switchHandler.processOFMessage(m);
+	}
+
+
+	/** Move channel from scratch to WAIT_INITIAL_STATE, then MASTER,
+	 * then SLAVE for cases where the switch does not support roles.
+	 * I.e., the final SLAVE transition should disconnect the switch.
+	 */
 	@Test
-    public void testInitialMoveToMasterTimeout() throws Exception {
-        int timeout = 50;
-        switchHandler.useRoleChangerWithOtherTimeoutForTesting(timeout);
-
-        // first, move us to WAIT_INITIAL_ROLE_STATE
-        moveToWaitInitialRole();
-        assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class));
-
-        // Set the role
-        setupSwitchSendRoleRequestAndVerify(null, OFControllerRole.ROLE_MASTER);
-        assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class));
-
-        // prepare mocks and inject the role reply message
-        reset(sw);
-        expect(sw.getOFFactory()).andReturn(factory).anyTimes();
-        sw.write(anyObject(OFMessage.class));
-        expectLastCall().anyTimes();
-        sw.write(anyObject(Iterable.class));
-        expectLastCall().anyTimes();
-        expect(sw.getTables()).andStubReturn((short)0);
-        sw.setAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, false);
-        expectLastCall().once();
-        sw.setControllerRole(OFControllerRole.ROLE_MASTER);
-        expectLastCall().once();
-        expect(sw.getStatus()).andReturn(SwitchStatus.HANDSHAKE).once();
-        sw.setStatus(SwitchStatus.MASTER);
-        expectLastCall().once();
-        replay(sw);
-
-        OFMessage m = factory.buildBarrierReply().build();
-
-        Thread.sleep(timeout+5);
-
-        reset(switchManager);
-        switchManager.switchStatusChanged(sw, SwitchStatus.HANDSHAKE, SwitchStatus.MASTER);
-        expectLastCall().once();
-        replay(switchManager);
-        switchHandler.processOFMessage(m);
-
-        assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.MasterState.class));
-
-    }
-
-
-    /** Move the channel from scratch to SLAVE state
-     * Builds on doMoveToWaitInitialRole()
-     * adds testing for WAIT_INITAL_ROLE state
-     *
-     * This method tests the case that the switch does NOT support roles.
-     * The channel handler still needs to send the initial request to find
-     * out that whether the switch supports roles.
-     *
-     */
-    @Test
-    public void testInitialMoveToSlaveNoRole() throws Exception {
-        // first, move us to WAIT_INITIAL_ROLE_STATE
-        moveToWaitInitialRole();
-        assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class));
-
-        // Set the role
-        long xid = setupSwitchSendRoleRequestAndVerify(null, OFControllerRole.ROLE_SLAVE);
-        assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class));
-
-        // prepare mocks and inject the role reply message
-        reset(sw);
-        sw.setAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, false);
-        expectLastCall().once();
-        sw.setControllerRole(OFControllerRole.ROLE_SLAVE);
-        expectLastCall().once();
-        sw.disconnect(); // Make sure we disconnect
-        expectLastCall().once();
-        replay(sw);
-
-
-        // FIXME: shouldn't use ordinal(), but OFError is broken
-
-        // Error with incorrect xid and type. Should be ignored.
-        OFMessage err = getBadActionErrorMessage(OFBadActionCode.BAD_TYPE, xid+1);
-
-        // sendMessageToHandler will verify and rest controller mock
-        switchHandler.processOFMessage(err);
-        assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class));
-
-        // Error with correct xid. Should trigger state transition
-        err = getBadRequestErrorMessage(OFBadRequestCode.BAD_EXPERIMENTER, xid);
-        // sendMessageToHandler will verify and rest controller mock
-        switchHandler.processOFMessage(err);
-    }
-
-
-    /** Move the channel from scratch to SLAVE state
-     * Builds on doMoveToWaitInitialRole()
-     * adds testing for WAIT_INITAL_ROLE state
-     *
-     * We let the initial role request time out. The switch should be
-     * disconnected
-     */
-    @Test
-    public void testInitialMoveToSlaveTimeout() throws Exception {
-        int timeout = 50;
-        switchHandler.useRoleChangerWithOtherTimeoutForTesting(timeout);
-
-        // first, move us to WAIT_INITIAL_ROLE_STATE
-        moveToWaitInitialRole();
-        assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class));
-
-        // Set the role
-        setupSwitchSendRoleRequestAndVerify(null, OFControllerRole.ROLE_SLAVE);
-        assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class));
-
-        // prepare mocks and inject the role reply message
-        reset(sw);
-        sw.setAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, false);
-        expectLastCall().once();
-        sw.setControllerRole(OFControllerRole.ROLE_SLAVE);
-        expectLastCall().once();
-        sw.disconnect(); // Make sure we disconnect
-        expectLastCall().once();
-        replay(sw);
-
-        // Apparently this can be any type of message for this test?!
-        OFMessage m = factory.buildBarrierReply().build();
-
-        Thread.sleep(timeout+5);
-        switchHandler.processOFMessage(m);
-    }
-
-
-    /** Move channel from scratch to WAIT_INITIAL_STATE, then MASTER,
-     * then SLAVE for cases where the switch does not support roles.
-     * I.e., the final SLAVE transition should disconnect the switch.
-     */
-    @Test
-    public void testNoRoleInitialToMasterToSlave() throws Exception {
-        int xid = 46;
-        // First, lets move the state to MASTER without role support
-        testInitialMoveToMasterNoRole();
-        assertThat(switchHandler.getStateForTesting(),
-                CoreMatchers.instanceOf(OFSwitchHandshakeHandler.MasterState.class));
-
-        assertThat("Unexpected messages have been captured",
-                connection.getMessages(),
-                 Matchers.empty());
-
-        // try to set master role again. should be a no-op
-        setupSwitchRoleChangeUnsupported(xid, OFControllerRole.ROLE_MASTER);
-        assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.MasterState.class));
-
-        assertThat("Unexpected messages have been captured",
-                connection.getMessages(),
-                 Matchers.empty());
-
-        setupSwitchRoleChangeUnsupported(xid, OFControllerRole.ROLE_SLAVE);
-        assertThat(connection.isConnected(), equalTo(false));
-
-        assertThat("Unexpected messages have been captured",
-                connection.getMessages(),
-                 Matchers.empty());
-    }
-
-    /** Move the channel to MASTER state
-     * Expects that the channel is in MASTER or SLAVE state.
-     *
-     */
-    @SuppressWarnings("unchecked")
+	public void testNoRoleInitialToMasterToSlave() throws Exception {
+		int xid = 46;
+		// First, lets move the state to MASTER without role support
+		testInitialMoveToMasterNoRole();
+		assertThat(switchHandler.getStateForTesting(),
+				CoreMatchers.instanceOf(OFSwitchHandshakeHandler.MasterState.class));
+
+		assertThat("Unexpected messages have been captured",
+				connection.getMessages(),
+				Matchers.empty());
+
+		// try to set master role again. should be a no-op
+		setupSwitchRoleChangeUnsupported(xid, OFControllerRole.ROLE_MASTER);
+		assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.MasterState.class));
+
+		assertThat("Unexpected messages have been captured",
+				connection.getMessages(),
+				Matchers.empty());
+
+		setupSwitchRoleChangeUnsupported(xid, OFControllerRole.ROLE_SLAVE);
+		assertThat(connection.isConnected(), equalTo(false));
+
+		assertThat("Unexpected messages have been captured",
+				connection.getMessages(),
+				Matchers.empty());
+	}
+
+	/** Move the channel to MASTER state
+	 * Expects that the channel is in MASTER or SLAVE state.
+	 *
+	 */
+	@SuppressWarnings("unchecked")
 	public void changeRoleToMasterWithRequest() throws Exception {
-        assertTrue("This method can only be called when handler is in " +
-                   "MASTER or SLAVE role", switchHandler.isHandshakeComplete());
-
-        // Set the role
-        long xid = setupSwitchSendRoleRequestAndVerify(true, OFControllerRole.ROLE_MASTER);
-
-        // prepare mocks and inject the role reply message
-        reset(sw);
-        expect(sw.getOFFactory()).andReturn(factory).anyTimes();
-        sw.write(anyObject(OFMessage.class));
-        expectLastCall().anyTimes();
-        sw.write(anyObject(Iterable.class));
-        expectLastCall().anyTimes();
-        expect(sw.getTables()).andStubReturn((short)0);
-        sw.setAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, true);
-        expectLastCall().once();
-        sw.setControllerRole(OFControllerRole.ROLE_MASTER);
-        expectLastCall().once();
-        expect(sw.getStatus()).andReturn(SwitchStatus.HANDSHAKE).once();
-        sw.setStatus(SwitchStatus.MASTER);
-        expectLastCall().once();
-        replay(sw);
-
-        reset(switchManager);
-        switchManager.switchStatusChanged(sw, SwitchStatus.HANDSHAKE, SwitchStatus.MASTER);
-        expectLastCall().once();
-        replay(switchManager);
-
-        OFMessage reply = getRoleReply(xid, OFControllerRole.ROLE_MASTER);
-
-        switchHandler.processOFMessage(reply);
-
-        assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.MasterState.class));
-    }
-
-    /** Move the channel to SLAVE state
-     * Expects that the channel is in MASTER or SLAVE state.
-     *
-     */
-    public void changeRoleToSlaveWithRequest() throws Exception {
-        assertTrue("This method can only be called when handler is in " +
-                   "MASTER or SLAVE role", switchHandler.isHandshakeComplete());
-
-        // Set the role
-        long xid = setupSwitchSendRoleRequestAndVerify(true, OFControllerRole.ROLE_SLAVE);
-
-        // prepare mocks and inject the role reply message
-        reset(sw);
-        sw.setAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, true);
-        expectLastCall().once();
-        sw.setControllerRole(OFControllerRole.ROLE_SLAVE);
-        expectLastCall().once();
-        expect(sw.getStatus()).andReturn(SwitchStatus.MASTER).once();
-        sw.setStatus(SwitchStatus.SLAVE);
-        expectLastCall().once();
-        replay(sw);
-
-        reset(switchManager);
-        switchManager.switchStatusChanged(sw, SwitchStatus.MASTER, SwitchStatus.SLAVE);
-        expectLastCall().once();
-        replay(switchManager);
-
-        OFMessage reply = getRoleReply(xid, OFControllerRole.ROLE_SLAVE);
-        connection.getListener().messageReceived(connection, reply);
-
-        assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.SlaveState.class));
-    }
-
-    @Test
-    public void testMultiRoleChange1() throws Exception {
-        testInitialMoveToMasterWithRole();
-        changeRoleToMasterWithRequest();
-        changeRoleToSlaveWithRequest();
-        changeRoleToSlaveWithRequest();
-        changeRoleToMasterWithRequest();
-        changeRoleToSlaveWithRequest();
-    }
-
-    @Test
-    public void testMultiRoleChange2() throws Exception {
-        testInitialMoveToSlaveWithRole();
-        changeRoleToMasterWithRequest();
-        changeRoleToSlaveWithRequest();
-        changeRoleToSlaveWithRequest();
-        changeRoleToMasterWithRequest();
-        changeRoleToSlaveWithRequest();
-    }
-
-    /** Start from scratch and reply with an unexpected error to the role
-     * change request
-     * Builds on doMoveToWaitInitialRole()
-     * adds testing for WAIT_INITAL_ROLE state
-     */
-    @Test
-    public void testInitialRoleChangeOtherError() throws Exception {
-        // first, move us to WAIT_INITIAL_ROLE_STATE
-        moveToWaitInitialRole();
-        assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class));
-
-        // Set the role
-        long xid = setupSwitchSendRoleRequestAndVerify(null, OFControllerRole.ROLE_MASTER);
-        assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class));
-
-        OFMessage err = getBadActionErrorMessage(OFBadActionCode.BAD_TYPE, xid);
-
-        verifyExceptionCaptured(err, SwitchStateException.class);
-    }
-
-    /**
-     * Test dispatch of messages while in MASTER role
-     */
-    @Test
-    public void testMessageDispatchMaster() throws Exception {
-        testInitialMoveToMasterWithRole();
-
-        // Send packet in. expect dispatch
-        OFPacketIn pi = factory.buildPacketIn()
-                .setReason(OFPacketInReason.NO_MATCH)
-                .build();
-        reset(switchManager);
-        switchManager.handleMessage(sw, pi, null);
-        expectLastCall().once();
-        replay(switchManager);
-        switchHandler.processOFMessage(pi);
-
-        // TODO: many more to go
-    }
-
-    /**
-     * Test port status message handling while MASTER
-     *
-     */
-    @Test
-    public void testPortStatusMessageMaster() throws Exception {
-        DatapathId dpid = featuresReply.getDatapathId();
-        testInitialMoveToMasterWithRole();
-
-        OFPortDesc portDesc = factory.buildPortDesc()
-                .setName("Port1")
-                .setPortNo(OFPort.of(1))
-                .build();
-        OFPortStatus.Builder portStatusBuilder = factory.buildPortStatus()
-                .setDesc(portDesc);
-
-        // The events we expect sw.handlePortStatus to return
-        // We'll just use the same list for all valid OFPortReasons and add
-        // arbitrary events for arbitrary ports that are not necessarily
-        // related to the port status message. Our goal
-        // here is not to return the correct set of events but the make sure
-        // that a) sw.handlePortStatus is called
-        //      b) the list of events sw.handlePortStatus returns is sent
-        //         as IOFSwitchListener notifications.
-        OrderedCollection<PortChangeEvent> events =
-                new LinkedHashSetWrapper<PortChangeEvent>();
-        OFPortDesc.Builder pb = factory.buildPortDesc();
-        OFPortDesc p1 = pb.setName("eth1").setPortNo(OFPort.of(1)).build();
-        OFPortDesc p2 = pb.setName("eth2").setPortNo(OFPort.of(2)).build();
-        OFPortDesc p3 = pb.setName("eth3").setPortNo(OFPort.of(3)).build();
-        OFPortDesc p4 = pb.setName("eth4").setPortNo(OFPort.of(4)).build();
-        OFPortDesc p5 = pb.setName("eth5").setPortNo(OFPort.of(5)).build();
-
-        
-        events.add(new PortChangeEvent(p1, PortChangeType.ADD));
-        events.add(new PortChangeEvent(p2, PortChangeType.DELETE));
-        events.add(new PortChangeEvent(p3, PortChangeType.UP));
-        events.add(new PortChangeEvent(p4, PortChangeType.DOWN));
-        events.add(new PortChangeEvent(p5, PortChangeType.OTHER_UPDATE));
-
-
-        for (OFPortReason reason: OFPortReason.values()) {
-            OFPortStatus portStatus = portStatusBuilder.setReason(reason).build();
-
-            reset(sw);
-            expect(sw.getId()).andReturn(dpid).anyTimes();
-
-            expect(sw.processOFPortStatus(portStatus)).andReturn(events).once();
-            replay(sw);
-
-            reset(switchManager);
-            switchManager.notifyPortChanged(sw, p1, PortChangeType.ADD);
-            switchManager.notifyPortChanged(sw, p2, PortChangeType.DELETE);
-            switchManager.notifyPortChanged(sw, p3, PortChangeType.UP);
-            switchManager.notifyPortChanged(sw, p4, PortChangeType.DOWN);
-            switchManager.notifyPortChanged(sw, p5, PortChangeType.OTHER_UPDATE);
-            replay(switchManager);
-
-            switchHandler.processOFMessage(portStatus);
-
-            verify(sw);
-        }
-    }
-
-    /**
-     * Test re-assert MASTER
-     *
-     */
-    @Test
-    public void testReassertMaster() throws Exception {
-        testInitialMoveToMasterWithRole();
-
-        OFMessage err = getBadRequestErrorMessage(OFBadRequestCode.EPERM, 42);
-
-        reset(roleManager);
-        roleManager.reassertRole(switchHandler, HARole.ACTIVE);
-        expectLastCall().once();
-        replay(roleManager);
-
-        reset(switchManager);
-        switchManager.handleMessage(sw, err, null);
-        expectLastCall().once();
-        replay(switchManager);
-
-        switchHandler.processOFMessage(err);
-
-        verify(sw);
-    }
-
-    /**
-     * Verify that the given exception event capture (as returned by
-     * getAndInitExceptionCapture) has thrown an exception of the given
-     * expectedExceptionClass.
-     * Resets the capture
-     * @param err
-     */
-    void verifyExceptionCaptured(
-            OFMessage err, Class<? extends Throwable> expectedExceptionClass) {
-
-        Throwable caughtEx = null;
-        // This should purposely cause an exception
-        try{
-            switchHandler.processOFMessage(err);
-        }
-        catch(Exception e){
-            // Capture the exception
-            caughtEx = e;
-        }
-
-        assertThat(caughtEx, CoreMatchers.instanceOf(expectedExceptionClass));
-    }
-
-    /**
-     * Tests the connection closed functionality before the switch handshake is complete.
-     * Essentially when the switch handshake is only aware of the IOFConnection.
-     */
-    @Test
-    public void testConnectionClosedBeforeHandshakeComplete() {
-
-        // Test connection closed prior to being finished
-        reset(switchManager);
-        switchManager.handshakeDisconnected(dpid);
-        expectLastCall().once();
-        replay(switchManager);
-
-        switchHandler.connectionClosed(connection);
-
-        verify(switchManager);
-    }
-
-    /**
-     * Tests the connection closed functionality after the switch handshake is complete.
-     * Essentially when the switch handshake is aware of an IOFSwitch.
-     * @throws Exception
-     */
-    @Test
-    public void testConnectionClosedAfterHandshakeComplete() throws Exception {
-
-        testInitialMoveToMasterWithRole();
-        // Test connection closed prior to being finished
-        reset(switchManager);
-        switchManager.handshakeDisconnected(dpid);
-        expectLastCall().once();
-        switchManager.switchDisconnected(sw);
-        expectLastCall().once();
-        replay(switchManager);
-
-        reset(sw);
-        expect(sw.getStatus()).andReturn(SwitchStatus.DISCONNECTED).anyTimes();
-        replay(sw);
-
-        switchHandler.connectionClosed(connection);
-
-        verify(switchManager);
-        verify(sw);
-    }
+		assertTrue("This method can only be called when handler is in " +
+				"MASTER or SLAVE role", switchHandler.isHandshakeComplete());
+
+		// Set the role
+		long xid = setupSwitchSendRoleRequestAndVerify(true, OFControllerRole.ROLE_MASTER);
+
+		// prepare mocks and inject the role reply message
+		reset(sw);
+		expect(sw.getOFFactory()).andReturn(factory).anyTimes();
+		sw.write(anyObject(OFMessage.class));
+		expectLastCall().anyTimes();
+		sw.write(anyObject(Iterable.class));
+		expectLastCall().anyTimes();
+		expect(sw.getNumTables()).andStubReturn((short)0);
+		sw.setAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, true);
+		expectLastCall().once();
+		sw.setControllerRole(OFControllerRole.ROLE_MASTER);
+		expectLastCall().once();
+		expect(sw.getStatus()).andReturn(SwitchStatus.HANDSHAKE).once();
+		sw.setStatus(SwitchStatus.MASTER);
+		expectLastCall().once();
+		expect(sw.getTables()).andReturn(Collections.EMPTY_LIST).once();
+		replay(sw);
+
+		reset(switchManager);
+		switchManager.switchStatusChanged(sw, SwitchStatus.HANDSHAKE, SwitchStatus.MASTER);
+		expectLastCall().once();
+		replay(switchManager);
+
+		OFMessage reply = getRoleReply(xid, OFControllerRole.ROLE_MASTER);
+
+		switchHandler.processOFMessage(reply);
+
+		assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.MasterState.class));
+	}
+
+	/** Move the channel to SLAVE state
+	 * Expects that the channel is in MASTER or SLAVE state.
+	 *
+	 */
+	public void changeRoleToSlaveWithRequest() throws Exception {
+		assertTrue("This method can only be called when handler is in " +
+				"MASTER or SLAVE role", switchHandler.isHandshakeComplete());
+
+		// Set the role
+		long xid = setupSwitchSendRoleRequestAndVerify(true, OFControllerRole.ROLE_SLAVE);
+
+		// prepare mocks and inject the role reply message
+		reset(sw);
+		sw.setAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, true);
+		expectLastCall().once();
+		sw.setControllerRole(OFControllerRole.ROLE_SLAVE);
+		expectLastCall().once();
+		expect(sw.getStatus()).andReturn(SwitchStatus.MASTER).once();
+		sw.setStatus(SwitchStatus.SLAVE);
+		expectLastCall().once();
+		replay(sw);
+
+		reset(switchManager);
+		switchManager.switchStatusChanged(sw, SwitchStatus.MASTER, SwitchStatus.SLAVE);
+		expectLastCall().once();
+		replay(switchManager);
+
+		OFMessage reply = getRoleReply(xid, OFControllerRole.ROLE_SLAVE);
+		connection.getListener().messageReceived(connection, reply);
+
+		assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.SlaveState.class));
+	}
+
+	@Test
+	public void testMultiRoleChange1() throws Exception {
+		testInitialMoveToMasterWithRole();
+		changeRoleToMasterWithRequest();
+		changeRoleToSlaveWithRequest();
+		changeRoleToSlaveWithRequest();
+		changeRoleToMasterWithRequest();
+		changeRoleToSlaveWithRequest();
+	}
+
+	@Test
+	public void testMultiRoleChange2() throws Exception {
+		testInitialMoveToSlaveWithRole();
+		changeRoleToMasterWithRequest();
+		changeRoleToSlaveWithRequest();
+		changeRoleToSlaveWithRequest();
+		changeRoleToMasterWithRequest();
+		changeRoleToSlaveWithRequest();
+	}
+
+	/** Start from scratch and reply with an unexpected error to the role
+	 * change request
+	 * Builds on doMoveToWaitInitialRole()
+	 * adds testing for WAIT_INITAL_ROLE state
+	 */
+	@Test
+	public void testInitialRoleChangeOtherError() throws Exception {
+		// first, move us to WAIT_INITIAL_ROLE_STATE
+		moveToWaitInitialRole();
+		assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class));
+
+		// Set the role
+		long xid = setupSwitchSendRoleRequestAndVerify(null, OFControllerRole.ROLE_MASTER);
+		assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class));
+
+		OFMessage err = getBadActionErrorMessage(OFBadActionCode.BAD_TYPE, xid);
+
+		verifyExceptionCaptured(err, SwitchStateException.class);
+	}
+
+	/**
+	 * Test dispatch of messages while in MASTER role
+	 */
+	@Test
+	public void testMessageDispatchMaster() throws Exception {
+		testInitialMoveToMasterWithRole();
+
+		// Send packet in. expect dispatch
+		OFPacketIn pi = factory.buildPacketIn()
+				.setReason(OFPacketInReason.NO_MATCH)
+				.build();
+		reset(switchManager);
+		switchManager.handleMessage(sw, pi, null);
+		expectLastCall().once();
+		replay(switchManager);
+		switchHandler.processOFMessage(pi);
+
+		// TODO: many more to go
+	}
+
+	/**
+	 * Test port status message handling while MASTER
+	 *
+	 */
+	@Test
+	public void testPortStatusMessageMaster() throws Exception {
+		DatapathId dpid = featuresReply.getDatapathId();
+		testInitialMoveToMasterWithRole();
+
+		OFPortDesc portDesc = factory.buildPortDesc()
+				.setName("Port1")
+				.setPortNo(OFPort.of(1))
+				.build();
+		OFPortStatus.Builder portStatusBuilder = factory.buildPortStatus()
+				.setDesc(portDesc);
+
+		// The events we expect sw.handlePortStatus to return
+		// We'll just use the same list for all valid OFPortReasons and add
+		// arbitrary events for arbitrary ports that are not necessarily
+		// related to the port status message. Our goal
+		// here is not to return the correct set of events but the make sure
+		// that a) sw.handlePortStatus is called
+		//      b) the list of events sw.handlePortStatus returns is sent
+		//         as IOFSwitchListener notifications.
+		OrderedCollection<PortChangeEvent> events =
+				new LinkedHashSetWrapper<PortChangeEvent>();
+		OFPortDesc.Builder pb = factory.buildPortDesc();
+		OFPortDesc p1 = pb.setName("eth1").setPortNo(OFPort.of(1)).build();
+		OFPortDesc p2 = pb.setName("eth2").setPortNo(OFPort.of(2)).build();
+		OFPortDesc p3 = pb.setName("eth3").setPortNo(OFPort.of(3)).build();
+		OFPortDesc p4 = pb.setName("eth4").setPortNo(OFPort.of(4)).build();
+		OFPortDesc p5 = pb.setName("eth5").setPortNo(OFPort.of(5)).build();
+
+
+		events.add(new PortChangeEvent(p1, PortChangeType.ADD));
+		events.add(new PortChangeEvent(p2, PortChangeType.DELETE));
+		events.add(new PortChangeEvent(p3, PortChangeType.UP));
+		events.add(new PortChangeEvent(p4, PortChangeType.DOWN));
+		events.add(new PortChangeEvent(p5, PortChangeType.OTHER_UPDATE));
+
+
+		for (OFPortReason reason: OFPortReason.values()) {
+			OFPortStatus portStatus = portStatusBuilder.setReason(reason).build();
+
+			reset(sw);
+			expect(sw.getId()).andReturn(dpid).anyTimes();
+
+			expect(sw.processOFPortStatus(portStatus)).andReturn(events).once();
+			replay(sw);
+
+			reset(switchManager);
+			switchManager.notifyPortChanged(sw, p1, PortChangeType.ADD);
+			switchManager.notifyPortChanged(sw, p2, PortChangeType.DELETE);
+			switchManager.notifyPortChanged(sw, p3, PortChangeType.UP);
+			switchManager.notifyPortChanged(sw, p4, PortChangeType.DOWN);
+			switchManager.notifyPortChanged(sw, p5, PortChangeType.OTHER_UPDATE);
+			replay(switchManager);
+
+			switchHandler.processOFMessage(portStatus);
+
+			verify(sw);
+		}
+	}
+
+	/**
+	 * Test re-assert MASTER
+	 *
+	 */
+	@Test
+	public void testReassertMaster() throws Exception {
+		testInitialMoveToMasterWithRole();
+
+		OFMessage err = getBadRequestErrorMessage(OFBadRequestCode.EPERM, 42);
+
+		reset(roleManager);
+		roleManager.reassertRole(switchHandler, HARole.ACTIVE);
+		expectLastCall().once();
+		replay(roleManager);
+
+		reset(switchManager);
+		switchManager.handleMessage(sw, err, null);
+		expectLastCall().once();
+		replay(switchManager);
+
+		switchHandler.processOFMessage(err);
+
+		verify(sw);
+	}
+
+	/**
+	 * Verify that the given exception event capture (as returned by
+	 * getAndInitExceptionCapture) has thrown an exception of the given
+	 * expectedExceptionClass.
+	 * Resets the capture
+	 * @param err
+	 */
+	void verifyExceptionCaptured(
+			OFMessage err, Class<? extends Throwable> expectedExceptionClass) {
+
+		Throwable caughtEx = null;
+		// This should purposely cause an exception
+		try{
+			switchHandler.processOFMessage(err);
+		}
+		catch(Exception e){
+			// Capture the exception
+			caughtEx = e;
+		}
+
+		assertThat(caughtEx, CoreMatchers.instanceOf(expectedExceptionClass));
+	}
+
+	/**
+	 * Tests the connection closed functionality before the switch handshake is complete.
+	 * Essentially when the switch handshake is only aware of the IOFConnection.
+	 */
+	@Test
+	public void testConnectionClosedBeforeHandshakeComplete() {
+
+		// Test connection closed prior to being finished
+		reset(switchManager);
+		switchManager.handshakeDisconnected(dpid);
+		expectLastCall().once();
+		replay(switchManager);
+
+		switchHandler.connectionClosed(connection);
+
+		verify(switchManager);
+	}
+
+	/**
+	 * Tests the connection closed functionality after the switch handshake is complete.
+	 * Essentially when the switch handshake is aware of an IOFSwitch.
+	 * @throws Exception
+	 */
+	@Test
+	public void testConnectionClosedAfterHandshakeComplete() throws Exception {
+
+		testInitialMoveToMasterWithRole();
+		// Test connection closed prior to being finished
+		reset(switchManager);
+		switchManager.handshakeDisconnected(dpid);
+		expectLastCall().once();
+		switchManager.switchDisconnected(sw);
+		expectLastCall().once();
+		replay(switchManager);
+
+		reset(sw);
+		expect(sw.getStatus()).andReturn(SwitchStatus.DISCONNECTED).anyTimes();
+		replay(sw);
+
+		switchHandler.connectionClosed(connection);
+
+		verify(switchManager);
+		verify(sw);
+	}
 }
diff --git a/src/test/java/net/floodlightcontroller/core/internal/OFSwitchManagerTest.java b/src/test/java/net/floodlightcontroller/core/internal/OFSwitchManagerTest.java
index 0a4ff826de222f4307ba98e4e83d2b305768206a..62d43ab96a1305cb89e0df40ae14df5ee9f89fd2 100644
--- a/src/test/java/net/floodlightcontroller/core/internal/OFSwitchManagerTest.java
+++ b/src/test/java/net/floodlightcontroller/core/internal/OFSwitchManagerTest.java
@@ -198,7 +198,7 @@ public class OFSwitchManagerTest{
         expect(sw.getSwitchDescription()).andReturn(description).anyTimes();
         expect(sw.getBuffers())
                 .andReturn(featuresReply.getNBuffers()).anyTimes();
-        expect(sw.getTables())
+        expect(sw.getNumTables())
                 .andReturn(featuresReply.getNTables()).anyTimes();
         expect(sw.getCapabilities())
                 .andReturn(featuresReply.getCapabilities()).anyTimes();
@@ -258,6 +258,7 @@ public class OFSwitchManagerTest{
         expect(sw.getStatus()).andReturn(SwitchStatus.MASTER).anyTimes();
         sw.disconnect();
         expectLastCall().once();
+        expect(sw.getOFFactory()).andReturn(factory).once();
         replay(sw, listener); // nothing recorded
         switchManager.switchAdded(sw);
         switchManager.switchStatusChanged(sw, SwitchStatus.HANDSHAKE, SwitchStatus.MASTER);
diff --git a/src/test/java/net/floodlightcontroller/core/test/MockSwitchManager.java b/src/test/java/net/floodlightcontroller/core/test/MockSwitchManager.java
index 2bd6d0c376585831fb8d37f273a0aa0ed2ae71c3..6e8f51ccfb243880ffd689ec3428e197fbbcc1be 100644
--- a/src/test/java/net/floodlightcontroller/core/test/MockSwitchManager.java
+++ b/src/test/java/net/floodlightcontroller/core/test/MockSwitchManager.java
@@ -90,6 +90,12 @@ public class MockSwitchManager implements IFloodlightModule, IOFSwitchManager, I
         // do nothing
 
     }
+    
+    @Override
+    public void handleOutgoingMessage(IOFSwitch sw, OFMessage m) {
+    	// do nothing
+    	
+    }
 
     public void setSwitchHandshakeHandlers(Map<DatapathId, OFSwitchHandshakeHandler> handlers) {
         this.switchHandlers = handlers;
diff --git a/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java
index 525fdcbe8f920e9f886ea113db31ceb1237caa3c..0bb86f00d0031948b33f5ef21382f50d09694aa3 100644
--- a/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java
+++ b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java
@@ -79,6 +79,7 @@ import net.floodlightcontroller.packet.Data;
 import net.floodlightcontroller.packet.Ethernet;
 import net.floodlightcontroller.packet.IPacket;
 import net.floodlightcontroller.packet.IPv4;
+import net.floodlightcontroller.packet.IPv6;
 import net.floodlightcontroller.packet.UDP;
 import net.floodlightcontroller.restserver.IRestApiService;
 import net.floodlightcontroller.restserver.RestApiServer;
@@ -99,6 +100,7 @@ import org.projectfloodlight.openflow.protocol.match.MatchField;
 import org.projectfloodlight.openflow.types.DatapathId;
 import org.projectfloodlight.openflow.types.EthType;
 import org.projectfloodlight.openflow.types.IPv4Address;
+import org.projectfloodlight.openflow.types.IPv6Address;
 import org.projectfloodlight.openflow.types.MacAddress;
 import org.projectfloodlight.openflow.types.OFBufferId;
 import org.projectfloodlight.openflow.types.OFPort;
@@ -118,8 +120,12 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 
 	protected OFPacketIn testARPReplyPacketIn_1, testARPReplyPacketIn_2;
 	protected OFPacketIn testUDPPacketIn;
+	protected OFPacketIn testUDPIPv6PacketIn;
+	protected OFPacketIn testUDPIPv6RevPacketIn;
 	protected IPacket testARPReplyPacket_1, testARPReplyPacket_2;
 	protected Ethernet testUDPPacket;
+	protected Ethernet testUDPIPv6Packet;
+	protected Ethernet testUDPIPv6RevPacket;
 	protected byte[] testARPReplyPacket_1_Srld, testARPReplyPacket_2_Srld;
 	protected byte[] testUDPPacketSrld;
 	private MockSyncService syncService;
@@ -243,10 +249,10 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 				.setHardwareAddressLength((byte) 6)
 				.setProtocolAddressLength((byte) 4)
 				.setOpCode(ARP.OP_REPLY)
-				.setSenderHardwareAddress(Ethernet.toMACAddress("00:44:33:22:11:01"))
-				.setSenderProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.1"))
-				.setTargetHardwareAddress(Ethernet.toMACAddress("00:11:22:33:44:55"))
-				.setTargetProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.2")));
+				.setSenderHardwareAddress(MacAddress.of("00:44:33:22:11:01"))
+				.setSenderProtocolAddress(IPv4Address.of("192.168.1.1"))
+				.setTargetHardwareAddress(MacAddress.of("00:11:22:33:44:55"))
+				.setTargetProtocolAddress(IPv4Address.of("192.168.1.2")));
 		this.testARPReplyPacket_1_Srld = testARPReplyPacket_1.serialize();
 
 		// Another test packet with the same ARP payload as packet 1 but with
@@ -263,10 +269,10 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 				.setHardwareAddressLength((byte) 6)
 				.setProtocolAddressLength((byte) 4)
 				.setOpCode(ARP.OP_REPLY)
-				.setSenderHardwareAddress(Ethernet.toMACAddress("00:44:33:22:11:01"))
-				.setSenderProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.1"))
-				.setTargetHardwareAddress(Ethernet.toMACAddress("00:11:22:33:44:55"))
-				.setTargetProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.2")));
+				.setSenderHardwareAddress(MacAddress.of("00:44:33:22:11:01"))
+				.setSenderProtocolAddress(IPv4Address.of("192.168.1.1"))
+				.setTargetHardwareAddress(MacAddress.of("00:11:22:33:44:55"))
+				.setTargetProtocolAddress(IPv4Address.of("192.168.1.2")));
 		this.testARPReplyPacket_2_Srld = testARPReplyPacket_2.serialize();
 
 		// This packet reverses the MACs and IP from testARPReplyPacket_1
@@ -286,6 +292,38 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 				.setPayload(new Data(new byte[] {0x01}))));
 		updateUDPPacketIn();
 
+		// This packet is used to test forming an entity from an IPv6 packet
+		this.testUDPIPv6Packet = (Ethernet) new Ethernet()
+		.setSourceMACAddress("00:11:22:33:44:55")
+		.setDestinationMACAddress("00:44:33:22:11:01")
+		.setEtherType(EthType.IPv6)
+		.setVlanID((short)5)
+		.setPayload(
+				new IPv6()
+				.setHopLimit((byte) 128)
+				.setSourceAddress(IPv6Address.of(1,1))
+				.setDestinationAddress(IPv6Address.of(2,2))
+				.setPayload(new UDP()
+				.setSourcePort((short) 5000)
+				.setDestinationPort((short) 5001)
+				.setPayload(new Data(new byte[] {0x01}))));
+
+		// This packet reverses the above
+		this.testUDPIPv6RevPacket = (Ethernet) new Ethernet()
+		.setSourceMACAddress("00:44:33:22:11:01")
+		.setDestinationMACAddress("00:11:22:33:44:55")
+		.setEtherType(EthType.IPv6)
+		.setVlanID((short)5)
+		.setPayload(
+				new IPv6()
+				.setHopLimit((byte) 128)
+				.setSourceAddress(IPv6Address.of(2,2))
+				.setDestinationAddress(IPv6Address.of(1,1))
+				.setPayload(new UDP()
+				.setSourcePort((short) 5001)
+				.setDestinationPort((short) 5000)
+				.setPayload(new Data(new byte[] {0x01}))));
+
 		// Build the PacketIn
 		this.testARPReplyPacketIn_1 = OFFactories.getFactory(OFVersion.OF_13).buildPacketIn()
 				.setBufferId(OFBufferId.NO_BUFFER)
@@ -301,6 +339,20 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 				.setData(this.testARPReplyPacket_2_Srld)
 				.setReason(OFPacketInReason.NO_MATCH)
 				.build();
+
+		this.testUDPIPv6PacketIn = OFFactories.getFactory(OFVersion.OF_13).buildPacketIn()
+				.setBufferId(OFBufferId.NO_BUFFER)
+				.setMatch(OFFactories.getFactory(OFVersion.OF_13).buildMatch().setExact(MatchField.IN_PORT, OFPort.of(1)).build())
+				.setData(this.testUDPIPv6Packet.serialize())
+				.setReason(OFPacketInReason.NO_MATCH)
+				.build();
+		
+		this.testUDPIPv6RevPacketIn = OFFactories.getFactory(OFVersion.OF_13).buildPacketIn()
+				.setBufferId(OFBufferId.NO_BUFFER)
+				.setMatch(OFFactories.getFactory(OFVersion.OF_13).buildMatch().setExact(MatchField.IN_PORT, OFPort.of(1)).build())
+				.setData(this.testUDPIPv6RevPacket.serialize())
+				.setReason(OFPacketInReason.NO_MATCH)
+				.build();
 	}
 
 	/**
@@ -322,9 +374,9 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 	public void testLastSeen() throws Exception {
 		Calendar c = Calendar.getInstance();
 		Date d1 = c.getTime();
-		Entity entity1 = new Entity(MacAddress.of(1L), null, null, null, null, d1);
+		Entity entity1 = new Entity(MacAddress.of(1L), VlanVid.ZERO /* untagged*/, IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO, d1);
 		c.add(Calendar.SECOND, 1);
-		Entity entity2 = new Entity(MacAddress.of(1L), null, IPv4Address.of(1), null, null, c.getTime());
+		Entity entity2 = new Entity(MacAddress.of(1L), VlanVid.ZERO /* untagged*/, IPv4Address.of(1), IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO, c.getTime());
 
 		IDevice d = deviceManager.learnDeviceByEntity(entity2);
 		assertEquals(c.getTime(), d.getLastSeen());
@@ -377,13 +429,13 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 
 		deviceManager.topology = mockTopology;
 
-		Entity entity1 = new Entity(MacAddress.of(1L), null, null, DatapathId.of(1L), OFPort.of(1), new Date());
-		Entity entity2 = new Entity(MacAddress.of(1L), null, null, DatapathId.of(10L), OFPort.of(1), new Date());
-		Entity entity3 = new Entity(MacAddress.of(1L), null, IPv4Address.of(1), DatapathId.of(10L), OFPort.of(1), new Date());
-		Entity entity4 = new Entity(MacAddress.of(1L), null, IPv4Address.of(1), DatapathId.of(1L), OFPort.of(1), new Date());
-		Entity entity5 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(4), IPv4Address.of(1), DatapathId.of(5L), OFPort.of(2), new Date());
-		Entity entity6 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(4), IPv4Address.of(1), DatapathId.of(50L), OFPort.of(3), new Date());
-		Entity entity7 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(4), IPv4Address.of(2), DatapathId.of(50L), OFPort.of(3), new Date());
+		Entity entity1 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(1L), OFPort.of(1), new Date());
+		Entity entity2 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(10L), OFPort.of(1), new Date());
+		Entity entity3 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.of(1), IPv6Address.NONE, DatapathId.of(10L), OFPort.of(1), new Date());
+		Entity entity4 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.of(1), IPv6Address.NONE, DatapathId.of(1L), OFPort.of(1), new Date());
+		Entity entity5 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(4), IPv4Address.of(1), IPv6Address.NONE, DatapathId.of(5L), OFPort.of(2), new Date());
+		Entity entity6 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(4), IPv4Address.of(1), IPv6Address.NONE, DatapathId.of(50L), OFPort.of(3), new Date());
+		Entity entity7 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(4), IPv4Address.of(2), IPv6Address.NONE, DatapathId.of(50L), OFPort.of(3), new Date());
 
 		mockListener.deviceAdded(isA(IDevice.class));
 		replay(mockListener, mockTopology);
@@ -393,7 +445,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		assertSame(d1, deviceManager.findDeviceByEntity(entity1));
 		assertEquals(DefaultEntityClassifier.entityClass ,
 				d1.getEntityClass());
-		assertArrayEquals(new VlanVid[] { VlanVid.ofVlan(-1) }, d1.getVlanId());
+		assertArrayEquals(new VlanVid[] { VlanVid.ZERO }, d1.getVlanId());
 		assertArrayEquals(new IPv4Address[] { }, d1.getIPv4Addresses());
 
 		assertEquals(1, deviceManager.getAllDevices().size());
@@ -408,8 +460,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		assertNotSame(d1, d2);
 		assertNotSame(d1.getDeviceKey(), d2.getDeviceKey());
 		assertEquals(MockEntityClassifier.testEC, d2.getEntityClass());
-		assertArrayEquals(new VlanVid[] { VlanVid.ofVlan(-1) }, d2.getVlanId());
-		assertArrayEquals(new Integer[] { }, d2.getIPv4Addresses());
+		assertArrayEquals(new VlanVid[] { VlanVid.ZERO }, d2.getVlanId());
+		assertArrayEquals(new IPv4Address[] { }, d2.getIPv4Addresses());
 
 		assertEquals(2, deviceManager.getAllDevices().size());
 		verify(mockListener);
@@ -428,7 +480,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 				d3.getAttachmentPoints());
 		assertArrayEquals(new SwitchPort[] { new SwitchPort(DatapathId.of(10L), OFPort.of(1)) },
 				d3.getAttachmentPoints(true));
-		assertArrayEquals(new VlanVid[] { VlanVid.ofVlan(-1) },
+		assertArrayEquals(new VlanVid[] { VlanVid.ZERO },
 				d3.getVlanId());
 
 		assertEquals(2, deviceManager.getAllDevices().size());
@@ -446,7 +498,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 				d4.getIPv4Addresses());
 		assertArrayEquals(new SwitchPort[] { new SwitchPort(DatapathId.of(1L), OFPort.of(1)) },
 				d4.getAttachmentPoints());
-		assertArrayEquals(new VlanVid[] { VlanVid.ofVlan(-1) },
+		assertArrayEquals(new VlanVid[] { VlanVid.ZERO },
 				d4.getVlanId());
 
 		assertEquals(2, deviceManager.getAllDevices().size());
@@ -504,15 +556,184 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 
 		deviceManager.entityClassifier = new MockEntityClassifierMac();
 		deviceManager.startUp(null);
-		Entity entityNoClass = new Entity(MacAddress.of(5L), VlanVid.ofVlan(1), IPv4Address.of(5), DatapathId.of(-1L), OFPort.of(1), new Date());
+		Entity entityNoClass = new Entity(MacAddress.of(5L), VlanVid.ofVlan(1), IPv4Address.of(5), IPv6Address.NONE, DatapathId.of(-1L), OFPort.of(1), new Date());
 		assertEquals(null, deviceManager.learnDeviceByEntity(entityNoClass));
 
 		verify(mockListener);
 	}
 
+	@Test
+	public void testEntityLearningIPv6() throws Exception {
+		IDeviceListener mockListener =
+				createMock(IDeviceListener.class);
+		expect(mockListener.getName()).andReturn("mockListener").atLeastOnce();
+		expect(mockListener.isCallbackOrderingPostreq((String)anyObject(), (String)anyObject()))
+		.andReturn(false).atLeastOnce();
+		expect(mockListener.isCallbackOrderingPrereq((String)anyObject(), (String)anyObject()))
+		.andReturn(false).atLeastOnce();
+
+		replay(mockListener);
+		deviceManager.addListener(mockListener);
+		verify(mockListener);
+		reset(mockListener);
+		deviceManager.entityClassifier= new MockEntityClassifier();
+		deviceManager.startUp(null);
+
+		ITopologyService mockTopology = createMock(ITopologyService.class);
+		expect(mockTopology.getL2DomainId(DatapathId.of(anyLong()))).
+		andReturn(DatapathId.of(1L)).anyTimes();
+		expect(mockTopology.isBroadcastDomainPort(DatapathId.of(anyLong()), OFPort.of(anyShort()))).
+		andReturn(false).anyTimes();
+
+		expect(mockTopology.isAttachmentPointPort(DatapathId.of(anyLong()),
+				OFPort.of(anyShort()))).andReturn(true).anyTimes();
+		expect(mockTopology.isConsistent(DatapathId.of(10L), OFPort.of(1), DatapathId.of(10L), OFPort.of(1))).
+		andReturn(true).anyTimes();
+		expect(mockTopology.isConsistent(DatapathId.of(1L), OFPort.of(1), DatapathId.of(1L), OFPort.of(1))).
+		andReturn(true).anyTimes();
+		expect(mockTopology.isConsistent(DatapathId.of(50L), OFPort.of(3), DatapathId.of(50L), OFPort.of(3))).
+		andReturn(true).anyTimes();
+
+		Date topologyUpdateTime = new Date();
+		expect(mockTopology.getLastUpdateTime()).andReturn(topologyUpdateTime).
+		anyTimes();
+
+		deviceManager.topology = mockTopology;
+
+		Entity entity1 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(1L), OFPort.of(1), new Date());
+		Entity entity2 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(10L), OFPort.of(1), new Date());
+		Entity entity3 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.of(1, 1), DatapathId.of(10L), OFPort.of(1), new Date());
+		Entity entity4 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.of(1, 1), DatapathId.of(1L), OFPort.of(1), new Date());
+		Entity entity5 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(4), IPv4Address.NONE, IPv6Address.of(1, 1), DatapathId.of(5L), OFPort.of(2), new Date());
+		Entity entity6 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(4), IPv4Address.NONE, IPv6Address.of(1, 1), DatapathId.of(50L), OFPort.of(3), new Date());
+		Entity entity7 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(4), IPv4Address.NONE, IPv6Address.of(2, 2), DatapathId.of(50L), OFPort.of(3), new Date());
+
+		mockListener.deviceAdded(isA(IDevice.class));
+		replay(mockListener, mockTopology);
+
+		Device d1 = deviceManager.learnDeviceByEntity(entity1);
+		assertSame(d1, deviceManager.learnDeviceByEntity(entity1));
+		assertSame(d1, deviceManager.findDeviceByEntity(entity1));
+		assertEquals(DefaultEntityClassifier.entityClass ,
+				d1.getEntityClass());
+		assertArrayEquals(new VlanVid[] { VlanVid.ZERO }, d1.getVlanId());
+		assertArrayEquals(new IPv6Address[] { }, d1.getIPv6Addresses());
+
+		assertEquals(1, deviceManager.getAllDevices().size());
+		verify(mockListener);
+
+		reset(mockListener);
+		mockListener.deviceAdded(isA(IDevice.class));
+		replay(mockListener);
+
+		Device d2 = deviceManager.learnDeviceByEntity(entity2);
+		assertFalse(d1.equals(d2));
+		assertNotSame(d1, d2);
+		assertNotSame(d1.getDeviceKey(), d2.getDeviceKey());
+		assertEquals(MockEntityClassifier.testEC, d2.getEntityClass());
+		assertArrayEquals(new VlanVid[] { VlanVid.ZERO }, d2.getVlanId());
+		assertArrayEquals(new IPv6Address[] { }, d2.getIPv6Addresses());
+
+		assertEquals(2, deviceManager.getAllDevices().size());
+		verify(mockListener);
+
+		reset(mockListener);
+		mockListener.deviceIPV6AddrChanged(isA(IDevice.class));
+		replay(mockListener);
+
+		Device d3 = deviceManager.learnDeviceByEntity(entity3);
+		assertNotSame(d2, d3);
+		assertEquals(d2.getDeviceKey(), d3.getDeviceKey());
+		assertEquals(MockEntityClassifier.testEC, d3.getEntityClass());
+		assertArrayEquals(new IPv6Address[] { IPv6Address.of(1, 1) },
+				d3.getIPv6Addresses());
+		assertArrayEquals(new SwitchPort[] { new SwitchPort(DatapathId.of(10L), OFPort.of(1)) },
+				d3.getAttachmentPoints());
+		assertArrayEquals(new SwitchPort[] { new SwitchPort(DatapathId.of(10L), OFPort.of(1)) },
+				d3.getAttachmentPoints(true));
+		assertArrayEquals(new VlanVid[] { VlanVid.ZERO },
+				d3.getVlanId());
+
+		assertEquals(2, deviceManager.getAllDevices().size());
+		verify(mockListener);
+
+		reset(mockListener);
+		mockListener.deviceIPV6AddrChanged(isA(IDevice.class));
+		replay(mockListener);
+
+		Device d4 = deviceManager.learnDeviceByEntity(entity4);
+		assertNotSame(d1, d4);
+		assertEquals(d1.getDeviceKey(), d4.getDeviceKey());
+		assertEquals(DefaultEntityClassifier.entityClass, d4.getEntityClass());
+		assertArrayEquals(new IPv6Address[] { IPv6Address.of(1, 1) },
+				d4.getIPv6Addresses());
+		assertArrayEquals(new SwitchPort[] { new SwitchPort(DatapathId.of(1L), OFPort.of(1)) },
+				d4.getAttachmentPoints());
+		assertArrayEquals(new VlanVid[] { VlanVid.ZERO },
+				d4.getVlanId());
+
+		assertEquals(2, deviceManager.getAllDevices().size());
+		verify(mockListener);
+
+		reset(mockListener);
+		mockListener.deviceAdded((isA(IDevice.class)));
+		replay(mockListener);
+
+		Device d5 = deviceManager.learnDeviceByEntity(entity5);
+		assertArrayEquals(new SwitchPort[] { new SwitchPort(DatapathId.of(5L), OFPort.of(2)) },
+				d5.getAttachmentPoints());
+		assertArrayEquals(new VlanVid[] { VlanVid.ofVlan(4) },
+				d5.getVlanId());
+		assertEquals(MacAddress.of(2L), d5.getMACAddress());
+		assertEquals("00:00:00:00:00:02", d5.getMACAddressString());
+		verify(mockListener);
+
+		reset(mockListener);
+		mockListener.deviceAdded(isA(IDevice.class));
+		replay(mockListener);
+
+		Device d6 = deviceManager.learnDeviceByEntity(entity6);
+		assertArrayEquals(new SwitchPort[] { new SwitchPort(DatapathId.of(50L), OFPort.of(3)) },
+				d6.getAttachmentPoints());
+		assertArrayEquals(new VlanVid[] { VlanVid.ofVlan(4) },
+				d6.getVlanId());
+
+		assertEquals(4, deviceManager.getAllDevices().size());
+		verify(mockListener);
+
+		reset(mockListener);
+		mockListener.deviceIPV6AddrChanged(isA(IDevice.class));
+		replay(mockListener);
+
+		Device d7 = deviceManager.learnDeviceByEntity(entity7);
+		assertNotSame(d6, d7);
+		assertEquals(d6.getDeviceKey(), d7.getDeviceKey());
+		assertArrayEquals(new SwitchPort[] { new SwitchPort(DatapathId.of(50L), OFPort.of(3)) },
+				d7.getAttachmentPoints());
+		assertArrayEquals(new VlanVid[] { VlanVid.ofVlan(4) },
+				d7.getVlanId());
+
+		assertEquals(4, deviceManager.getAllDevices().size());
+		verify(mockListener);
+
+		reset(mockListener);
+		replay(mockListener);
+
+		reset(deviceManager.topology);
+		deviceManager.topology.addListener(deviceManager);
+		expectLastCall().times(1);
+		replay(deviceManager.topology);
+
+		deviceManager.entityClassifier = new MockEntityClassifierMac();
+		deviceManager.startUp(null);
+		Entity entityNoClass = new Entity(MacAddress.of(5L), VlanVid.ofVlan(1), IPv4Address.NONE, IPv6Address.of(5, 5), DatapathId.of(-1L), OFPort.of(1), new Date());
+		assertEquals(null, deviceManager.learnDeviceByEntity(entityNoClass));
+
+		verify(mockListener);
+	}
 
 	private void doTestEntityOrdering(boolean computeInsertionPoint) throws Exception {
-		Entity e = new Entity(MacAddress.of(10L), null, null, null, null, null);
+		Entity e = new Entity(MacAddress.of(10L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO, Entity.NO_DATE);
 		IEntityClass ec = createNiceMock(IEntityClass.class);
 		Device d = new Device(deviceManager, 1L, e, ec);
 
@@ -528,7 +749,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		};
 
 		for (Long mac: macs) {
-			e = new Entity(MacAddress.of(mac), null, null, null, null, null);
+			e = new Entity(MacAddress.of(mac), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO, Entity.NO_DATE);
 			int insertionPoint;
 			if (computeInsertionPoint) {
 				insertionPoint = -(Arrays.binarySearch(d.entities, e)+1);
@@ -600,14 +821,14 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		deviceManager.topology = mockTopology;
 
 		Calendar c = Calendar.getInstance();
-		Entity entity1 = new Entity(MacAddress.of(1L), null, IPv4Address.of(1), DatapathId.of(1L), OFPort.of(1), c.getTime());
-		Entity entity0 = new Entity(MacAddress.of(1L), null, null, null, null, c.getTime());
+		Entity entity1 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.of(1), IPv6Address.NONE, DatapathId.of(1L), OFPort.of(1), c.getTime());
+		Entity entity0 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO, c.getTime());
 		c.add(Calendar.SECOND, 1);
-		Entity entity2 = new Entity(MacAddress.of(1L), null, null, DatapathId.of(5L), OFPort.of(1), c.getTime());
+		Entity entity2 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(5L), OFPort.of(1), c.getTime());
 		c.add(Calendar.SECOND, 1);
-		Entity entity3 = new Entity(MacAddress.of(1L), null, null, DatapathId.of(10L), OFPort.of(1), c.getTime());
+		Entity entity3 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(10L), OFPort.of(1), c.getTime());
 		c.add(Calendar.SECOND, 1);
-		Entity entity4 = new Entity(MacAddress.of(1L), null, null, DatapathId.of(50L), OFPort.of(1), c.getTime());
+		Entity entity4 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(50L), OFPort.of(1), c.getTime());
 
 		IDevice d;
 		SwitchPort[] aps;
@@ -664,106 +885,6 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		verify(mockListener);
 	}
 
-	/**
-	 * In this test, a device is moved from attachment point (1,1) to (5,1)
-	 * and then moved back to (1,1) within 30 seconds.  Both the moves should
-	 * generate device moved notification.
-	 * @throws Exception
-	 */
-	@Test
-	public void testAttachmentPointMovingBack() throws Exception {
-		IDeviceListener mockListener =
-				createMock(IDeviceListener.class);
-		expect(mockListener.getName()).andReturn("mockListener").atLeastOnce();
-		expect(mockListener.isCallbackOrderingPostreq((String)anyObject(), (String)anyObject()))
-		.andReturn(false).atLeastOnce();
-		expect(mockListener.isCallbackOrderingPrereq((String)anyObject(), (String)anyObject()))
-		.andReturn(false).atLeastOnce();
-
-		replay(mockListener);
-		deviceManager.addListener(mockListener);
-		verify(mockListener);
-		reset(mockListener);
-
-		ITopologyService mockTopology = createMock(ITopologyService.class);
-		expect(mockTopology.getL2DomainId(DatapathId.of(1L))).
-		andReturn(DatapathId.of(1L)).anyTimes();
-		expect(mockTopology.getL2DomainId(DatapathId.of(5L))).
-		andReturn(DatapathId.of(1L)).anyTimes();
-		expect(mockTopology.isBroadcastDomainPort(DatapathId.of(anyLong()), OFPort.of(anyShort()))).
-		andReturn(false).anyTimes();
-		expect(mockTopology.isInSameBroadcastDomain(DatapathId.of(anyLong()), OFPort.of(anyShort()),
-				DatapathId.of(anyLong()), OFPort.of(anyShort())))
-				.andReturn(false).anyTimes();
-
-		expect(mockTopology.isAttachmentPointPort(DatapathId.of(anyLong()),
-				OFPort.of(anyShort()))).andReturn(true).anyTimes();
-		expect(mockTopology.isConsistent(DatapathId.of(1L), OFPort.of(1), DatapathId.of(5L), OFPort.of(1))).
-		andReturn(false).anyTimes();
-
-		Date topologyUpdateTime = new Date();
-		expect(mockTopology.getLastUpdateTime()).andReturn(topologyUpdateTime).
-		anyTimes();
-
-		replay(mockTopology);
-
-		deviceManager.topology = mockTopology;
-
-		Calendar c = Calendar.getInstance();
-		Entity entity1 = new Entity(MacAddress.of(1L), null, null, DatapathId.of(1L), OFPort.of(1), c.getTime());
-		c.add(Calendar.SECOND, 1);
-		Entity entity2 = new Entity(MacAddress.of(1L), null, null, DatapathId.of(5L), OFPort.of(1), c.getTime());
-		c.add(Calendar.SECOND, 1);
-		Entity entity3 = new Entity(MacAddress.of(1L), null, null, DatapathId.of(1L), OFPort.of(1), c.getTime());
-		c.add(Calendar.SECOND, 1);
-		Entity entity4 = new Entity(MacAddress.of(1L), null, null, DatapathId.of(5L), OFPort.of(1), c.getTime());
-
-		IDevice d;
-		SwitchPort[] aps;
-
-		mockListener.deviceAdded(isA(IDevice.class));
-		replay(mockListener);
-
-		d = deviceManager.learnDeviceByEntity(entity1);
-		assertEquals(1, deviceManager.getAllDevices().size());
-		aps = d.getAttachmentPoints();
-		assertArrayEquals(new SwitchPort[] { new SwitchPort(DatapathId.of(1L), OFPort.of(1)) }, aps);
-		verify(mockListener);
-
-		reset(mockListener);
-		mockListener.deviceMoved((isA(IDevice.class)));
-		replay(mockListener);
-
-		d = deviceManager.learnDeviceByEntity(entity2);
-		assertEquals(1, deviceManager.getAllDevices().size());
-		aps = d.getAttachmentPoints();
-
-		assertArrayEquals(new SwitchPort[] { new SwitchPort(DatapathId.of(5L), OFPort.of(1)) }, aps);
-		verify(mockListener);
-
-		reset(mockListener);
-		mockListener.deviceMoved((isA(IDevice.class)));
-		replay(mockListener);
-
-		d = deviceManager.learnDeviceByEntity(entity3);
-		assertEquals(1, deviceManager.getAllDevices().size());
-		assertArrayEquals(new SwitchPort[] { new SwitchPort(DatapathId.of(1L), OFPort.of(1)),
-				new SwitchPort(DatapathId.of(5L), OFPort.of(1), ErrorStatus.DUPLICATE_DEVICE)},
-				d.getAttachmentPoints(true));
-		verify(mockListener);
-
-		// Generate a packet-in again from 5,1 and ensure that it doesn't
-		// create a device moved event.
-		reset(mockListener);
-		replay(mockListener);
-		d = deviceManager.learnDeviceByEntity(entity4);
-		assertEquals(1, deviceManager.getAllDevices().size());
-		assertArrayEquals(new SwitchPort[] { new SwitchPort(DatapathId.of(1L), OFPort.of(1)),
-				new SwitchPort(DatapathId.of(5L), OFPort.of(1), ErrorStatus.DUPLICATE_DEVICE)},
-				d.getAttachmentPoints(true));
-		verify(mockListener);
-	}
-
 	private void verifyEntityArray(Entity[] expected, Device d) {
 		Arrays.sort(expected);
 		assertArrayEquals(expected, d.entities);
@@ -818,13 +939,13 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		deviceManager.topology = mockTopology;
 
 		Calendar c = Calendar.getInstance();
-		Entity entity1 = new Entity(MacAddress.of(1L), null, IPv4Address.of(1), DatapathId.of(1L), OFPort.of(1), c.getTime());
+		Entity entity1 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.of(1), IPv6Address.NONE, DatapathId.of(1L), OFPort.of(1), c.getTime());
 		c.add(Calendar.SECOND, 1);
-		Entity entity2 = new Entity(MacAddress.of(1L), null, IPv4Address.of(2), DatapathId.of(2L), OFPort.of(1), c.getTime());
+		Entity entity2 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.of(2), IPv6Address.NONE, DatapathId.of(2L), OFPort.of(1), c.getTime());
 		c.add(Calendar.SECOND, 1);
-		Entity entity3 = new Entity(MacAddress.of(1L), null, IPv4Address.of(3), DatapathId.of(3L), OFPort.of(1), c.getTime());
+		Entity entity3 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.of(3), IPv6Address.NONE, DatapathId.of(3L), OFPort.of(1), c.getTime());
 		c.add(Calendar.SECOND, 1);
-		Entity entity4 = new Entity(MacAddress.of(1L), null, IPv4Address.of(4), DatapathId.of(4L), OFPort.of(1), c.getTime());
+		Entity entity4 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.of(4), IPv6Address.NONE, DatapathId.of(4L), OFPort.of(1), c.getTime());
 
 		IDevice d;
 		SwitchPort[] aps;
@@ -910,7 +1031,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		reset(mockListener);
 
 		ITopologyService mockTopology = createMock(ITopologyService.class);
-		expect(mockTopology.getL2DomainId(DatapathId.of(1L))).
+		expect(mockTopology.getL2DomainId(DatapathId.of(1L))). /* two different OpenFlow islands, 1 and 10 */
 		andReturn(DatapathId.of(1L)).anyTimes();
 		expect(mockTopology.getL2DomainId(DatapathId.of(5L))).
 		andReturn(DatapathId.of(1L)).anyTimes();
@@ -941,42 +1062,41 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		deviceManager.addSuppressAPs(DatapathId.of(10L), OFPort.of(1));
 
 		Calendar c = Calendar.getInstance();
-		Entity entity0 = new Entity(MacAddress.of(1L), null, null, null, null, c.getTime());
-		// No attachment point should be learnt on 1L, 1
-		Entity entity1 = new Entity(MacAddress.of(1L), null, IPv4Address.of(1), DatapathId.of(1L), OFPort.of(1), c.getTime());
+		/* OpenFlow island 1 */
+		Entity entity0 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO, c.getTime());
+		Entity entity1 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.of(1), IPv6Address.NONE, DatapathId.of(1L), OFPort.of(1), c.getTime()); /* suppressed */
 		c.add(Calendar.SECOND, 1);
-		Entity entity2 = new Entity(MacAddress.of(1L), null, IPv4Address.of(1), DatapathId.of(5L), OFPort.of(1), c.getTime());
+		Entity entity2 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.of(1), IPv6Address.NONE, DatapathId.of(5L), OFPort.of(1), c.getTime());
 		c.add(Calendar.SECOND, 1);
-		Entity entity3 = new Entity(MacAddress.of(1L), null, null, DatapathId.of(10L), OFPort.of(1), c.getTime());
+		/* OpenFlow island 10 */
+		Entity entity3 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(10L), OFPort.of(1), c.getTime()); /* suppressed */
 		c.add(Calendar.SECOND, 1);
-		Entity entity4 = new Entity(MacAddress.of(1L), null, null, DatapathId.of(50L), OFPort.of(1), c.getTime());
+		Entity entity4 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(50L), OFPort.of(1), c.getTime());
 
 		IDevice d;
 		SwitchPort[] aps;
 		IPv4Address[] ips;
 
+		/* When the device is first added we should expect an add event */
 		mockListener.deviceAdded(isA(IDevice.class));
+		/* It's also given an IPv4 address when the second entity is learned, so listeners should be notified */
 		mockListener.deviceIPV4AddrChanged((isA(IDevice.class)));
 		replay(mockListener);
 
-		// TODO: we currently do learn entities on suppressed APs
-		// // cannot learn device on suppressed AP
-		// d = deviceManager.learnDeviceByEntity(entity1);
-		// assertNull(d);
-
+		/* Device is learned w/o attachment point */
 		deviceManager.learnDeviceByEntity(entity0);
+		/* Device is "seen" with a suppressed AP now, so we shouldn't learn the AP but still keep the entity */
 		d = deviceManager.learnDeviceByEntity(entity1);
 		assertEquals(1, deviceManager.getAllDevices().size());
 		aps = d.getAttachmentPoints();
 		assertEquals(aps.length, 0);
-		verifyEntityArray(new Entity[] { entity0, entity1} , (Device)d);
+		verifyEntityArray(new Entity[] { entity0 , entity1} , (Device)d);
 		ips = d.getIPv4Addresses();
 		assertArrayEquals(new IPv4Address[] { IPv4Address.of(1) }, ips);
 		verify(mockListener);
 
 		reset(mockListener);
 		mockListener.deviceMoved((isA(IDevice.class)));
-		//mockListener.deviceIPV4AddrChanged((isA(IDevice.class)));
 		replay(mockListener);
 		d = deviceManager.learnDeviceByEntity(entity2);
 		assertEquals(1, deviceManager.getAllDevices().size());
@@ -990,7 +1110,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		reset(mockListener);
 		replay(mockListener);
 
-		d = deviceManager.learnDeviceByEntity(entity3);
+		d = deviceManager.learnDeviceByEntity(entity3); 
 		assertEquals(1, deviceManager.getAllDevices().size());
 		aps = d.getAttachmentPoints();
 		assertArrayEquals(new SwitchPort[] { new SwitchPort(DatapathId.of(5L), OFPort.of(1)) }, aps);
@@ -1039,13 +1159,13 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		deviceManager.topology = mockTopology;
 
 		Calendar c = Calendar.getInstance();
-		Entity entity1 = new Entity(MacAddress.of(1L), null, IPv4Address.of(1), DatapathId.of(1L), OFPort.of(1), c.getTime());
+		Entity entity1 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.of(1), IPv6Address.NONE, DatapathId.of(1L), OFPort.of(1), c.getTime());
 		c.add(Calendar.MILLISECOND,
 				(int)AttachmentPoint.OPENFLOW_TO_EXTERNAL_TIMEOUT/ 2);
-		Entity entity2 = new Entity(MacAddress.of(1L), null, null, DatapathId.of(1L), OFPort.of(2), c.getTime());
+		Entity entity2 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(1L), OFPort.of(2), c.getTime());
 		c.add(Calendar.MILLISECOND,
 				(int)AttachmentPoint.OPENFLOW_TO_EXTERNAL_TIMEOUT / 2 + 1);
-		Entity entity3 = new Entity(MacAddress.of(1L), null, null, DatapathId.of(1L), OFPort.of(2), c.getTime());
+		Entity entity3 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(1L), OFPort.of(2), c.getTime());
 
 		IDevice d;
 		SwitchPort[] aps;
@@ -1105,13 +1225,13 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		deviceManager.topology = mockTopology;
 
 		Calendar c = Calendar.getInstance();
-		Entity entity1 = new Entity(MacAddress.of(1L), null, IPv4Address.of(1), DatapathId.of(1L), OFPort.of(1), c.getTime());
+		Entity entity1 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.of(1), IPv6Address.NONE, DatapathId.of(1L), OFPort.of(1), c.getTime());
 		c.add(Calendar.MILLISECOND,
 				(int)AttachmentPoint.OPENFLOW_TO_EXTERNAL_TIMEOUT/ 2);
-		Entity entity2 = new Entity(MacAddress.of(1L), null, null, DatapathId.of(1L), OFPort.LOCAL, c.getTime());
+		Entity entity2 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(1L), OFPort.LOCAL, c.getTime());
 		c.add(Calendar.MILLISECOND,
 				(int)AttachmentPoint.OPENFLOW_TO_EXTERNAL_TIMEOUT + 1);
-		Entity entity3 = new Entity(MacAddress.of(1L), null, null, DatapathId.of(1L), OFPort.of(2), c.getTime());
+		Entity entity3 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(1L), OFPort.of(2), c.getTime());
 
 		IDevice d;
 		SwitchPort[] aps;
@@ -1178,23 +1298,25 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 	 * Vlan and ip are optional all other fields must be specified.
 	 * @return
 	 */
-	private static void verifyDevice(IDevice d, long mac, Short vlan, Integer ip,
-			long swId, int port) {
+	private static void verifyDevice(IDevice d, MacAddress mac, VlanVid vlan, IPv4Address ipv4,
+			IPv6Address ipv6, DatapathId swId, OFPort port) {
 		assertNotNull(d);
-		assertEquals(MacAddress.of(mac), d.getMACAddress());
-		if (vlan == null)
-			assertArrayEquals(new VlanVid[] { VlanVid.ofVlan(-1) }, d.getVlanId());
-		else
-			assertArrayEquals(new VlanVid[] { VlanVid.ofVlan(vlan) }, d.getVlanId());
-
-		if (ip == null)
-			assertArrayEquals(new IPv4Address[] { IPv4Address.of(0) }, d.getIPv4Addresses());
-		else
-			assertArrayEquals(new IPv4Address[] { IPv4Address.of(ip) }, d.getIPv4Addresses());
-
-		SwitchPort expectedAp = new SwitchPort(DatapathId.of(swId), OFPort.of(port));
-		assertArrayEquals(new SwitchPort[] { expectedAp },
-				d.getAttachmentPoints());
+		if (!mac.equals(MacAddress.NONE)) {
+			assertEquals(mac, d.getMACAddress());
+		}
+		if (vlan != null) {
+			assertArrayEquals(new VlanVid[] { vlan }, d.getVlanId());
+		}
+		if (!ipv4.equals(IPv4Address.NONE)) {
+			assertArrayEquals(new IPv4Address[] { ipv4 }, d.getIPv4Addresses());
+		}
+		if (!ipv6.equals(IPv6Address.NONE)) {
+			assertArrayEquals(new IPv6Address[] { ipv6 }, d.getIPv6Addresses());
+		}
+		if (!swId.equals(DatapathId.NONE) && !port.equals(OFPort.ZERO)) {
+			SwitchPort expectedAp = new SwitchPort(swId, port);
+			assertArrayEquals(new SwitchPort[] { expectedAp }, d.getAttachmentPoints());
+		}
 	}
 
 
@@ -1203,7 +1325,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		MacAddress deviceMac =
 				((Ethernet)this.testARPReplyPacket_1).getSourceMACAddress();
 		OFPacketIn packetIn = testARPReplyPacketIn_1;
-		Integer ipaddr = IPv4.toIPv4Address("192.168.1.1");
+		IPv4Address ipaddr = IPv4Address.of("192.168.1.1");
 
 		// Mock up our expected behavior
 		ITopologyService mockTopology = createMock(ITopologyService.class);
@@ -1218,9 +1340,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		// Verify the device
 		Device rdevice = (Device)
 				deviceManager.findDevice(deviceMac,
-						VlanVid.ofVlan(5), null, null, null);
-		verifyDevice(rdevice, deviceMac.getLong(),
-				(short) 5, ipaddr, 1L, 1);
+						VlanVid.ofVlan(5), IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO);
+		verifyDevice(rdevice, deviceMac, VlanVid.ofVlan(5), ipaddr, IPv6Address.NONE, DatapathId.of(1), OFPort.of(1));
 		IDevice cntxSrcDev = IDeviceService.fcStore.get(cntx,
 				IDeviceService.CONTEXT_SRC_DEVICE);
 		assertEquals(rdevice, cntxSrcDev);
@@ -1230,8 +1351,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 
 		Device result = null;
 		Iterator<? extends IDevice> dstiter =
-				deviceManager.queryDevices(null, null, IPv4Address.of(ipaddr),
-						null, null);
+				deviceManager.queryDevices(MacAddress.NONE, null /* any VLAN here */, ipaddr,
+						IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO);
 		if (dstiter.hasNext()) {
 			result = (Device)dstiter.next();
 		}
@@ -1257,9 +1378,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		// Verify the device
 		rdevice = (Device)
 				deviceManager.findDevice(deviceMac,
-						VlanVid.ofVlan(5), null, null, null);
-		verifyDevice(rdevice, deviceMac.getLong(),
-				(short)5, ipaddr, 5L, 2);
+						VlanVid.ofVlan(5), IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO);
+		verifyDevice(rdevice, deviceMac, VlanVid.ofVlan(5), ipaddr, IPv6Address.NONE, DatapathId.of(5), OFPort.of(2));
 		cntxSrcDev = IDeviceService.fcStore.get(cntx,
 				IDeviceService.CONTEXT_SRC_DEVICE);
 		assertEquals(rdevice, cntxSrcDev);
@@ -1285,13 +1405,115 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 
 		assertEquals(Command.CONTINUE, cmd);
 		IDevice srcDev =
-				deviceManager.findDevice(srcMac, VlanVid.ofVlan(5), null, null, null);
-		verifyDevice(srcDev, srcMac.getLong(), (short)5, null,
-				1L, testUDPPacketIn.getMatch().get(MatchField.IN_PORT).getShortPortNumber());
+				deviceManager.findDevice(srcMac, VlanVid.ofVlan(5), IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO);
+		verifyDevice(srcDev, srcMac, VlanVid.ofVlan(5), IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(1), testUDPPacketIn.getMatch().get(MatchField.IN_PORT));
 
 		IDevice dstDev =
-				deviceManager.findDevice(dstMac, VlanVid.ofVlan(5), null, null, null);
-		verifyDevice(dstDev, dstMac.getLong(), (short)5, ipaddr, 5L, 2);
+				deviceManager.findDevice(dstMac, VlanVid.ofVlan(5), IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO);
+		verifyDevice(dstDev, dstMac, VlanVid.ofVlan(5), ipaddr, IPv6Address.NONE, DatapathId.of(5), OFPort.of(2));
+
+		cntxSrcDev = IDeviceService.fcStore.get(cntx,
+				IDeviceService.CONTEXT_SRC_DEVICE);
+		assertEquals(srcDev, cntxSrcDev);
+
+		cntxDstDev = IDeviceService.fcStore.get(cntx,
+				IDeviceService.CONTEXT_DST_DEVICE);
+		assertEquals(dstDev, cntxDstDev);
+
+		assertEquals(2, deviceManager.getAllDevices().size());
+	}
+
+	@Test
+	public void testPacketInBasicIPv6() throws Exception {
+		MacAddress deviceMac =
+				((Ethernet)this.testUDPIPv6Packet).getSourceMACAddress();
+		OFPacketIn packetIn = testUDPIPv6PacketIn;
+		IPv6Address ipaddr = IPv6Address.of(1, 1);
+
+		// Mock up our expected behavior
+		ITopologyService mockTopology = createMock(ITopologyService.class);
+		deviceManager.topology = mockTopology;
+		mockTopologyForPacketInTests(mockTopology);
+		replay(mockTopology);
+
+		FloodlightContext cntx = new FloodlightContext();
+		Command cmd = dispatchPacketIn(1L, packetIn, cntx);
+		verify(mockTopology);
+		assertEquals(Command.CONTINUE, cmd);
+		// Verify the device
+		Device rdevice = (Device)
+				deviceManager.findDevice(deviceMac,
+						VlanVid.ofVlan(5), IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO);
+		verifyDevice(rdevice, deviceMac, VlanVid.ofVlan(5), IPv4Address.NONE, ipaddr, DatapathId.of(1), OFPort.of(1));
+		IDevice cntxSrcDev = IDeviceService.fcStore.get(cntx,
+				IDeviceService.CONTEXT_SRC_DEVICE);
+		assertEquals(rdevice, cntxSrcDev);
+		IDevice cntxDstDev = IDeviceService.fcStore.get(cntx,
+				IDeviceService.CONTEXT_DST_DEVICE);
+		assertNull(cntxDstDev);
+
+		Device result = null;
+		Iterator<? extends IDevice> dstiter =
+				deviceManager.queryDevices(MacAddress.NONE, null /* any VLAN here */, IPv4Address.NONE,
+						ipaddr, DatapathId.NONE, OFPort.ZERO);
+		if (dstiter.hasNext()) {
+			result = (Device)dstiter.next();
+		}
+		assertFalse("There shouldn't be more than 1 device", dstiter.hasNext());
+		assertEquals(rdevice, result);
+
+
+		//-----------------
+		// Test packetIn again with a different source port. Should be
+		// the same device
+		reset(mockTopology);
+		mockTopologyForPacketInTests(mockTopology);
+		replay(mockTopology);
+
+		// trigger the packet in
+		cntx = new FloodlightContext();
+		packetIn = packetIn.createBuilder().setMatch(OFFactories.getFactory(OFVersion.OF_13).buildMatch().setExact(MatchField.IN_PORT, OFPort.of(2)).build()).build();
+		cmd = dispatchPacketIn(5L, packetIn, cntx);
+		verify(mockTopology);
+		// Verify the replay matched our expectations
+		assertEquals(Command.CONTINUE, cmd);
+
+		// Verify the device
+		rdevice = (Device)
+				deviceManager.findDevice(deviceMac,
+						VlanVid.ofVlan(5), IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO);
+		verifyDevice(rdevice, deviceMac, VlanVid.ofVlan(5), IPv4Address.NONE, ipaddr, DatapathId.of(5), OFPort.of(2));
+		cntxSrcDev = IDeviceService.fcStore.get(cntx,
+				IDeviceService.CONTEXT_SRC_DEVICE);
+		assertEquals(rdevice, cntxSrcDev);
+		cntxDstDev = IDeviceService.fcStore.get(cntx,
+				IDeviceService.CONTEXT_DST_DEVICE);
+		assertNull(cntxDstDev);
+		// There can be only one device
+		assertEquals(1, deviceManager.getAllDevices().size());
+
+		//----------------------------
+		// Test packetIn with a different packet going the reverse direction.
+		// We should now get source and dest device in the context
+		//==> The destination device in this step has been learned just before
+		MacAddress srcMac = testUDPIPv6RevPacket.getSourceMACAddress();
+		MacAddress dstMac = deviceMac;
+		reset(mockTopology);
+		mockTopologyForPacketInTests(mockTopology);
+		replay(mockTopology);
+		// trigger the packet in
+		cntx = new FloodlightContext();
+		cmd = dispatchPacketIn(1L, testUDPIPv6RevPacketIn, cntx);
+		verify(mockTopology);
+
+		assertEquals(Command.CONTINUE, cmd);
+		IDevice srcDev =
+				deviceManager.findDevice(srcMac, VlanVid.ofVlan(5), IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO);
+		verifyDevice(srcDev, srcMac, VlanVid.ofVlan(5), IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(1), testUDPIPv6PacketIn.getMatch().get(MatchField.IN_PORT));
+
+		IDevice dstDev =
+				deviceManager.findDevice(dstMac, VlanVid.ofVlan(5), IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO);
+		verifyDevice(dstDev, dstMac, VlanVid.ofVlan(5), IPv4Address.NONE, ipaddr, DatapathId.of(5), OFPort.of(2));
 
 		cntxSrcDev = IDeviceService.fcStore.get(cntx,
 				IDeviceService.CONTEXT_SRC_DEVICE);
@@ -1315,11 +1537,11 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 	@Test
 	public void testDeviceLearningFromArpResponseData() throws Exception {
 		ARP arp = (ARP)((Ethernet)this.testARPReplyPacket_2).getPayload();
-		MacAddress senderMac = MacAddress.of(arp.getSenderHardwareAddress());
+		MacAddress senderMac = arp.getSenderHardwareAddress();
 		MacAddress sourceMac =
 				((Ethernet)this.testARPReplyPacket_2)
 				.getSourceMACAddress();
-		Integer ipaddr = IPv4.toIPv4Address("192.168.1.1");
+		IPv4Address ipaddr = IPv4Address.of("192.168.1.1");
 		OFPacketIn packetIn = testARPReplyPacketIn_2;
 
 		// Mock up our expected behavior
@@ -1334,14 +1556,14 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		verify(mockTopology);
 		assertEquals(Command.CONTINUE, cmd);
 		// Verify the device for the sender HW address
-		Device senderDev = (Device)
-				deviceManager.findDevice(senderMac, VlanVid.ofVlan(5), null, null, null);
-		verifyDevice(senderDev, senderMac.getLong(), (short)5, ipaddr, 1L, 1);
+		IDevice senderDev = (Device)
+				deviceManager.findDevice(senderMac, VlanVid.ofVlan(5), IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO);
+		verifyDevice(senderDev, senderMac, VlanVid.ofVlan(5), ipaddr, IPv6Address.NONE, DatapathId.of(1), OFPort.of(1));
 
-		Device result = null;
+		IDevice result = null;
 		Iterator<? extends IDevice> dstiter =
-				deviceManager.queryDevices(null, null, IPv4Address.of(ipaddr),
-						null, null);
+				deviceManager.queryDevices(MacAddress.NONE, null /* any VLAN here */, ipaddr,
+						IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO);
 		if (dstiter.hasNext()) {
 			result = (Device)dstiter.next();
 		}
@@ -1351,10 +1573,10 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 
 
 		// Verify the device for the source MAC
-		Device srcDev = (Device)
-				deviceManager.findDevice(sourceMac, VlanVid.ofVlan(5), null, null, null);
+		IDevice srcDev = (Device)
+				deviceManager.findDevice(sourceMac, VlanVid.ofVlan(5), IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO);
 		// must NOT learn IP on this device
-		verifyDevice(srcDev, sourceMac.getLong(), (short)5, null, 1L, 1);
+		verifyDevice(srcDev, sourceMac, VlanVid.ofVlan(5), IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.of(1));
 		assertFalse("Device must differ", srcDev.equals(senderDev));
 		// Context is annotated with this device, not the device associated
 		// with ARP sender address
@@ -1416,8 +1638,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		assertEquals(Command.CONTINUE, cmd);
 		cntxSrcDev = IDeviceService.fcStore.get(cntx,
 				IDeviceService.CONTEXT_SRC_DEVICE);
-		verifyDevice(cntxSrcDev, 1L, (short)5, null,
-				1L, testUDPPacketIn.getMatch().get(MatchField.IN_PORT).getShortPortNumber());
+		verifyDevice(cntxSrcDev, MacAddress.of(1), VlanVid.ofVlan(5), IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(1), testUDPPacketIn.getMatch().get(MatchField.IN_PORT));
 
 		cntxDstDev = IDeviceService.fcStore.get(cntx,
 				IDeviceService.CONTEXT_DST_DEVICE);
@@ -1443,8 +1664,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		assertEquals(Command.CONTINUE, cmd);
 		IDevice cntxSrcDev = IDeviceService.fcStore.get(cntx,
 				IDeviceService.CONTEXT_SRC_DEVICE);
-		verifyDevice(cntxSrcDev, srcMac.getLong(), (short)5, null,
-				1L, testUDPPacketIn.getMatch().get(MatchField.IN_PORT).getShortPortNumber());
+		verifyDevice(cntxSrcDev, srcMac, VlanVid.ofVlan(5), IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(1), testUDPPacketIn.getMatch().get(MatchField.IN_PORT));
 		IDevice cntxDstDev = IDeviceService.fcStore.get(cntx,
 				IDeviceService.CONTEXT_DST_DEVICE);
 		assertNull(cntxDstDev);
@@ -1460,9 +1680,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 				IDeviceService.CONTEXT_SRC_DEVICE);
 		// yes: we check that cntxSrcDev matched dstMAC because we are
 		// just adding the dest device
-		int ip = IPv4.toIPv4Address("192.168.1.1");
-		verifyDevice(cntxSrcDev, dstMac.getLong(), (short)5, ip,
-				1L, testARPReplyPacketIn_1.getMatch().get(MatchField.IN_PORT).getShortPortNumber());
+		IPv4Address ip = IPv4Address.of("192.168.1.1");
+		verifyDevice(cntxSrcDev, dstMac, VlanVid.ofVlan(5), ip, IPv6Address.NONE, DatapathId.of(1), testARPReplyPacketIn_1.getMatch().get(MatchField.IN_PORT));
 		// yes: we set the expected dst device to the current srcDev
 		IDevice expectedDstDev = cntxSrcDev;
 
@@ -1513,7 +1732,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 
 		expect(mockTopology.isBroadcastDomainPort(DatapathId.of(1L), OFPort.of(1))).andReturn(false).anyTimes();
 		expect(mockTopology.isBroadcastDomainPort(DatapathId.of(5L), OFPort.of(1))).andReturn(false).anyTimes();
-		expect(mockTopology.getL2DomainId(DatapathId.of(1L))).andReturn(DatapathId.of(1L)).anyTimes();
+		expect(mockTopology.getL2DomainId(DatapathId.of(1L))).andReturn(DatapathId.of(1L)).anyTimes(); /* different islands */
 		expect(mockTopology.getL2DomainId(DatapathId.of(5L))).andReturn(DatapathId.of(5L)).anyTimes();
 		expect(mockTopology.isConsistent(DatapathId.of(1L), OFPort.of(1), DatapathId.of(5L), OFPort.of(1))).
 		andReturn(false).anyTimes();
@@ -1526,23 +1745,23 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		deviceManager.topology = mockTopology;
 
 		Calendar c = Calendar.getInstance();
-		Entity entity1 = new Entity(MacAddress.of(1L), null, IPv4Address.of(2), DatapathId.of(1L), OFPort.of(1), c.getTime());
-		c.add(Calendar.MILLISECOND, -DeviceManagerImpl.ENTITY_TIMEOUT-1);
-		Entity entity2 = new Entity(MacAddress.of(1L), null, IPv4Address.of(1), DatapathId.of(5L), OFPort.of(1), c.getTime());
+		Entity entity1 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.of(2), IPv6Address.NONE, DatapathId.of(1L), OFPort.of(1), c.getTime());
+		c.add(Calendar.MILLISECOND, -DeviceManagerImpl.ENTITY_TIMEOUT-1); /* go back in time s.t. entity1 is 1ms away from timing out WRT entity2 */
+		Entity entity2 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.of(1), IPv6Address.NONE, DatapathId.of(5L), OFPort.of(1), c.getTime());
 
 		deviceManager.learnDeviceByEntity(entity1);
-		IDevice d = deviceManager.learnDeviceByEntity(entity2);
+		IDevice d = deviceManager.learnDeviceByEntity(entity2); /* learn entity2 from "the past." */
 		assertArrayEquals(new IPv4Address[] { IPv4Address.of(1), IPv4Address.of(2) }, d.getIPv4Addresses());
 		assertArrayEquals(new SwitchPort[] { new SwitchPort(DatapathId.of(1L), OFPort.of(1)),
 				new SwitchPort(DatapathId.of(5L), OFPort.of(1))},
 				d.getAttachmentPoints());
 		Iterator<? extends IDevice> diter =
 				deviceManager.queryClassDevices(d.getEntityClass(),
-						null, null, IPv4Address.of(1), null, null);
+						MacAddress.NONE, VlanVid.ZERO /* untagged here */, IPv4Address.of(1), IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO);
 		assertTrue(diter.hasNext());
 		assertEquals(d.getDeviceKey(), diter.next().getDeviceKey());
 		diter = deviceManager.queryClassDevices(d.getEntityClass(),
-				null, null, IPv4Address.of(2), null, null);
+				MacAddress.NONE, VlanVid.ZERO /* untagged here */, IPv4Address.of(2), IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO);
 		assertTrue(diter.hasNext());
 		assertEquals(d.getDeviceKey(), diter.next().getDeviceKey());
 
@@ -1550,7 +1769,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		deviceManager.addListener(mockListener);
 		verify(mockListener);
 		reset(mockListener);
-
+		mockListener.deviceMoved(isA(IDevice.class)); /* the device really should move, since it's losing an attachment point along with having its DPID changed */
 		mockListener.deviceIPV4AddrChanged(isA(IDevice.class));
 		replay(mockListener);
 		deviceManager.entityCleanupTask.reschedule(0, null);
@@ -1563,14 +1782,14 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 				new SwitchPort(DatapathId.of(5L), OFPort.of(1)) },
 				d.getAttachmentPoints());
 		diter = deviceManager.queryClassDevices(d.getEntityClass(),
-				null, null, IPv4Address.of(2), null, null);
+				MacAddress.NONE, VlanVid.ZERO /* untagged here */, IPv4Address.of(2), IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO);
 		assertTrue(diter.hasNext());
 		assertEquals(d.getDeviceKey(), diter.next().getDeviceKey());
 		diter = deviceManager.queryClassDevices(d.getEntityClass(),
-				null, null, IPv4Address.of(1), null, null);
+				MacAddress.NONE, VlanVid.ZERO /* untagged here */, IPv4Address.of(1), IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO);
 		assertFalse(diter.hasNext());
 
-		d = deviceManager.findDevice(MacAddress.of(1L), null, null, null, null);
+		d = deviceManager.findDevice(MacAddress.of(1L), VlanVid.ZERO /* untagged here */, IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO);
 		assertArrayEquals(new IPv4Address[] { IPv4Address.of(2) }, d.getIPv4Addresses());
 
 		// Attachment points are not removed, previous ones are still valid.
@@ -1592,8 +1811,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 
 		Calendar c = Calendar.getInstance();
 		c.add(Calendar.MILLISECOND, -DeviceManagerImpl.ENTITY_TIMEOUT-1);
-		Entity entity1 = new Entity(MacAddress.of(1L), null, IPv4Address.of(1), DatapathId.of(1L), OFPort.of(1), c.getTime());
-		Entity entity2 = new Entity(MacAddress.of(1L), null, IPv4Address.of(2), DatapathId.of(5L), OFPort.of(1), c.getTime());
+		Entity entity1 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.of(1), IPv6Address.NONE, DatapathId.of(1L), OFPort.of(1), c.getTime());
+		Entity entity2 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.of(2), IPv6Address.NONE, DatapathId.of(5L), OFPort.of(1), c.getTime());
 
 		ITopologyService mockTopology = createMock(ITopologyService.class);
 		deviceManager.topology = mockTopology;
@@ -1631,10 +1850,10 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		assertNull(r);
 		Iterator<? extends IDevice> diter =
 				deviceManager.queryClassDevices(d.getEntityClass(),
-						null, null, IPv4Address.of(1), null, null);
+						MacAddress.NONE, VlanVid.ZERO, IPv4Address.of(1), IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO);
 		assertFalse(diter.hasNext());
 
-		r = deviceManager.findDevice(MacAddress.of(1L), null, null, null, null);
+		r = deviceManager.findDevice(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO);
 		assertNull(r);
 
 		verify(mockListener);
@@ -1680,13 +1899,15 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 						Entity newEntity = new Entity (e.macAddress,
 								e.vlan,
 								e.ipv4Address,
+								e.ipv6Address,
 								e.switchDPID,
 								e.switchPort,
 								e.lastSeenTimestamp);
-						if (e.vlan == null)
+						if (newEntity.vlan.equals(VlanVid.ZERO)) {
 							newEntity.vlan = VlanVid.ofVlan(1);
-						else
-							newEntity.vlan = VlanVid.ofVlan(((e.vlan.getVlan() + 1 % 4095)+1));
+						} else {
+							newEntity.vlan = VlanVid.ofVlan((e.vlan.getVlan() + 1 % 4095) + 1);
+						}
 						newDevice = new Device(newDevice, newEntity, -1);
 					}
 					assertEquals(false, newDevice.equals(d));
@@ -1772,19 +1993,18 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		replay(mockTopology);
 		deviceManager.topology = mockTopology;
 
-		Entity entity1 = new Entity(MacAddress.of(1L), null, null, DatapathId.of(1L), OFPort.of(1), c.getTime());
-		Entity entity1a = new Entity(MacAddress.of(1L), null, IPv4Address.of(1), DatapathId.of(1L), OFPort.of(1), c.getTime());
-		Entity entity2 = new Entity(MacAddress.of(1L), null, null, DatapathId.of(5L), OFPort.of(1), c.getTime());
-		Entity entity3 = new Entity(MacAddress.of(1L), null, null, DatapathId.of(10L), OFPort.of(1), c.getTime());
-		entity1.setLastSeenTimestamp(c.getTime());
+		/* All w/same activeSince stamp, c.getTime() */
+		Entity entity1 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(1L), OFPort.of(1), c.getTime());
+		Entity entity1a = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.of(1), IPv6Address.NONE, DatapathId.of(1L), OFPort.of(1), c.getTime());
+		Entity entity2 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(5L), OFPort.of(1), c.getTime());
+		Entity entity3 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(10L), OFPort.of(1), c.getTime());
+		entity1.setLastSeenTimestamp(c.getTime()); /* same as activeSince */
 		c.add(Calendar.MILLISECOND, Entity.ACTIVITY_TIMEOUT/2);
-		entity1a.setLastSeenTimestamp(c.getTime());
+		entity1a.setLastSeenTimestamp(c.getTime()); /* halfway timed out for entity1 */
 		c.add(Calendar.MILLISECOND, 1);
-		entity2.setLastSeenTimestamp(c.getTime());
+		entity2.setLastSeenTimestamp(c.getTime()); /* small increment; still below entity1 timeout */
 		c.add(Calendar.MILLISECOND, 1);
-		entity3.setLastSeenTimestamp(c.getTime());
-
-
+		entity3.setLastSeenTimestamp(c.getTime()); /* small increment; still below entity1 timeout */
 
 		IDevice d;
 		d = deviceManager.learnDeviceByEntity(entity1);
@@ -1800,21 +2020,21 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 				d.getAttachmentPoints(true));
 
 		c.add(Calendar.MILLISECOND, Entity.ACTIVITY_TIMEOUT/4);
-		entity1.setLastSeenTimestamp(c.getTime());
-		d = deviceManager.learnDeviceByEntity(entity1);
+		entity1.setLastSeenTimestamp(c.getTime()); /* entity1 now is 3/4 the way to a timeout, but still is active */
+		d = deviceManager.learnDeviceByEntity(entity1); /* we're going back to an old AP here within its active window (the "flapping") */
 
 		// all are still active; entity3 should still win
-		assertArrayEquals(new SwitchPort[] { new SwitchPort(DatapathId.of(1L), OFPort.of(1)) },
+		assertArrayEquals(new SwitchPort[] { new SwitchPort(DatapathId.of(10L), OFPort.of(1)) },
 				d.getAttachmentPoints());
-		assertArrayEquals(new SwitchPort[] { new SwitchPort(DatapathId.of(1L), OFPort.of(1)),
+		assertArrayEquals(new SwitchPort[] { new SwitchPort(DatapathId.of(10L), OFPort.of(1)),
 				new SwitchPort(DatapathId.of(5L), OFPort.of(1),
 						ErrorStatus.DUPLICATE_DEVICE),
-						new SwitchPort(DatapathId.of(10L), OFPort.of(1),
+						new SwitchPort(DatapathId.of(1L), OFPort.of(1),
 								ErrorStatus.DUPLICATE_DEVICE) },
 								d.getAttachmentPoints(true));
 
 		c.add(Calendar.MILLISECOND, Entity.ACTIVITY_TIMEOUT+2000);
-		entity1.setLastSeenTimestamp(c.getTime());
+		entity1.setLastSeenTimestamp(c.getTime()); /* now "relearn" entity1 for a time beyond all others' timeouts; thus entity1 should be the only remaining AP */
 		d = deviceManager.learnDeviceByEntity(entity1);
 
 		assertEquals(entity1.getActiveSince(), entity1.getLastSeenTimestamp());
@@ -1864,10 +2084,10 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		replay(mockTopology);
 		deviceManager.topology = mockTopology;
 
-		Entity entity1 = new Entity(MacAddress.of(1L), null, null, DatapathId.of(1L), OFPort.of(1), c.getTime());
-		Entity entity2 = new Entity(MacAddress.of(1L), null, null, DatapathId.of(1L), OFPort.of(2), c.getTime());
-		Entity entity3 = new Entity(MacAddress.of(1L), null, null, DatapathId.of(5L), OFPort.of(1), c.getTime());
-		Entity entity4 = new Entity(MacAddress.of(1L), null, null, DatapathId.of(5L), OFPort.of(2), c.getTime());
+		Entity entity1 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(1L), OFPort.of(1), c.getTime());
+		Entity entity2 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(1L), OFPort.of(2), c.getTime());
+		Entity entity3 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(5L), OFPort.of(1), c.getTime());
+		Entity entity4 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(5L), OFPort.of(2), c.getTime());
 		entity1.setLastSeenTimestamp(c.getTime());
 		c.add(Calendar.MILLISECOND, Entity.ACTIVITY_TIMEOUT/2);
 		c.add(Calendar.MILLISECOND, 1);
@@ -1895,12 +2115,12 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		d = deviceManager.learnDeviceByEntity(entity1);
 
 		// all entities are active, so entities 2,4 should win
-		assertArrayEquals(new SwitchPort[] { new SwitchPort(DatapathId.of(1L), OFPort.of(1)),
+		assertArrayEquals(new SwitchPort[] { new SwitchPort(DatapathId.of(1L), OFPort.of(2)),
 				new SwitchPort(DatapathId.of(5L), OFPort.of(2)) },
 				d.getAttachmentPoints());
-		assertArrayEquals(new SwitchPort[] { new SwitchPort(DatapathId.of(1L), OFPort.of(1)),
+		assertArrayEquals(new SwitchPort[] { new SwitchPort(DatapathId.of(1L), OFPort.of(2)),
 				new SwitchPort(DatapathId.of(5L), OFPort.of(2)),
-				new SwitchPort(DatapathId.of(1L), OFPort.of(2), ErrorStatus.DUPLICATE_DEVICE)},
+				new SwitchPort(DatapathId.of(1L), OFPort.of(1), ErrorStatus.DUPLICATE_DEVICE)},
 				d.getAttachmentPoints(true));
 
 		c.add(Calendar.MILLISECOND, Entity.ACTIVITY_TIMEOUT+1);
@@ -1929,11 +2149,11 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 	}
 
 	protected void doTestDeviceQuery() throws Exception {
-		Entity entity1 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(1), IPv4Address.of(1), DatapathId.of(1L), OFPort.of(1), new Date());
-		Entity entity2 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.of(2), DatapathId.of(1L), OFPort.of(2), new Date());
-		Entity entity3 = new Entity(MacAddress.of(3L), VlanVid.ofVlan(3), IPv4Address.of(3), DatapathId.of(5L), OFPort.of(1), new Date());
-		Entity entity4 = new Entity(MacAddress.of(4L), VlanVid.ofVlan(4), IPv4Address.of(3), DatapathId.of(5L), OFPort.of(2), new Date());
-		Entity entity5 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(4), IPv4Address.of(3), DatapathId.of(5L), OFPort.of(2), new Date());
+		Entity entity1 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(1), IPv4Address.of(1), IPv6Address.NONE, DatapathId.of(1L), OFPort.of(1), new Date());
+		Entity entity2 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.of(2), IPv6Address.NONE, DatapathId.of(1L), OFPort.of(2), new Date());
+		Entity entity3 = new Entity(MacAddress.of(3L), VlanVid.ofVlan(3), IPv4Address.of(3), IPv6Address.NONE, DatapathId.of(5L), OFPort.of(1), new Date());
+		Entity entity4 = new Entity(MacAddress.of(4L), VlanVid.ofVlan(4), IPv4Address.of(3), IPv6Address.NONE, DatapathId.of(5L), OFPort.of(2), new Date());
+		Entity entity5 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(4), IPv4Address.of(3), IPv6Address.NONE, DatapathId.of(5L), OFPort.of(2), new Date());
 
 		Device d1 = deviceManager.learnDeviceByEntity(entity1);
 		deviceManager.learnDeviceByEntity(entity2);
@@ -1943,7 +2163,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		IDevice d;
 
 		Iterator<? extends IDevice> iter =
-				deviceManager.queryDevices(null, VlanVid.ofVlan(1), IPv4Address.of(1), null, null);
+				deviceManager.queryDevices(MacAddress.NONE, VlanVid.ofVlan(1), IPv4Address.of(1), IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO);
 		int count = 0;
 		while (iter.hasNext()) {
 			count += 1;
@@ -1952,7 +2172,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		}
 		assertEquals(1, count);
 
-		iter = deviceManager.queryDevices(null, VlanVid.ofVlan(3), IPv4Address.of(3), null, null);
+		iter = deviceManager.queryDevices(MacAddress.NONE, VlanVid.ofVlan(3), IPv4Address.of(3), IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO);
 		count = 0;
 		while (iter.hasNext()) {
 			count += 1;
@@ -1961,7 +2181,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		}
 		assertEquals(1, count);
 
-		iter = deviceManager.queryDevices(null, VlanVid.ofVlan(1), IPv4Address.of(3), null, null);
+		iter = deviceManager.queryDevices(MacAddress.NONE, VlanVid.ofVlan(1), IPv4Address.of(3), IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO);
 		count = 0;
 		while (iter.hasNext()) {
 			count += 1;
@@ -1970,7 +2190,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		assertEquals(0, count);
 
 		Device d5 = deviceManager.learnDeviceByEntity(entity5);
-		iter = deviceManager.queryDevices(null, VlanVid.ofVlan(4), IPv4Address.of(3), null, null);
+		iter = deviceManager.queryDevices(MacAddress.NONE, VlanVid.ofVlan(4), IPv4Address.of(3), IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO);
 		count = 0;
 		Set<Long> deviceKeysFromIterator = new HashSet<Long>();
 		while (iter.hasNext()) {
@@ -1985,7 +2205,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		assertEquals(2, count);
 
 
-		iter = deviceManager.queryDevices(MacAddress.of(1L), null, null, null, null);
+		iter = deviceManager.queryDevices(MacAddress.of(1L), null /* don't care (want 1 and 4) NOT VlanVid.ZERO-->untagged */, 
+				IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO);
 		count = 0;
 		deviceKeysFromIterator = new HashSet<Long>();
 		while (iter.hasNext()) {
@@ -2004,7 +2225,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 	public void testDeviceIndex() throws Exception {
 		EnumSet<IDeviceService.DeviceField> indexFields =
 				EnumSet.noneOf(IDeviceService.DeviceField.class);
-		indexFields.add(IDeviceService.DeviceField.IPV4);
+		indexFields.add(IDeviceService.DeviceField.IPv4);
 		indexFields.add(IDeviceService.DeviceField.VLAN);
 		deviceManager.addIndex(false, indexFields);
 
@@ -2035,11 +2256,11 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 	}
 
 	protected void doTestDeviceClassQuery() throws Exception {
-		Entity entity1 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(1), IPv4Address.of(1), DatapathId.of(1L), OFPort.of(1), new Date());
-		Entity entity2 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.of(2), DatapathId.of(1L), OFPort.of(2), new Date());
-		Entity entity3 = new Entity(MacAddress.of(3L), VlanVid.ofVlan(3), IPv4Address.of(3), DatapathId.of(5L), OFPort.of(1), new Date());
-		Entity entity4 = new Entity(MacAddress.of(4L), VlanVid.ofVlan(4), IPv4Address.of(3), DatapathId.of(5L), OFPort.of(2), new Date());
-		Entity entity5 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(4), IPv4Address.of(3), DatapathId.of(5L), OFPort.of(2), new Date());
+		Entity entity1 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(1), IPv4Address.of(1), IPv6Address.NONE, DatapathId.of(1L), OFPort.of(1), new Date());
+		Entity entity2 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.of(2), IPv6Address.NONE, DatapathId.of(1L), OFPort.of(2), new Date());
+		Entity entity3 = new Entity(MacAddress.of(3L), VlanVid.ofVlan(3), IPv4Address.of(3), IPv6Address.NONE, DatapathId.of(5L), OFPort.of(1), new Date());
+		Entity entity4 = new Entity(MacAddress.of(4L), VlanVid.ofVlan(4), IPv4Address.of(3), IPv6Address.NONE, DatapathId.of(5L), OFPort.of(2), new Date());
+		Entity entity5 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(4), IPv4Address.of(3), IPv6Address.NONE, DatapathId.of(5L), OFPort.of(2), new Date());
 
 		IDevice d1 = deviceManager.learnDeviceByEntity(entity1);
 		IDevice d2 = deviceManager.learnDeviceByEntity(entity2);
@@ -2052,8 +2273,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		IDevice d;
 
 		Iterator<? extends IDevice> iter =
-				deviceManager.queryClassDevices(d1.getEntityClass(), null,
-						VlanVid.ofVlan(1), IPv4Address.of(1), null, null);
+				deviceManager.queryClassDevices(d1.getEntityClass(), MacAddress.NONE,
+						VlanVid.ofVlan(1), IPv4Address.of(1), IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO);
 		int count = 0;
 		while (iter.hasNext()) {
 			count += 1;
@@ -2062,8 +2283,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		}
 		assertEquals(1, count);
 
-		iter = deviceManager.queryClassDevices(d1.getEntityClass(), null,
-				VlanVid.ofVlan(3), IPv4Address.of(3), null, null);
+		iter = deviceManager.queryClassDevices(d1.getEntityClass(), MacAddress.NONE,
+				VlanVid.ofVlan(3), IPv4Address.of(3), IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO);
 		count = 0;
 		while (iter.hasNext()) {
 			count += 1;
@@ -2073,8 +2294,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		}
 		assertEquals(1, count);
 
-		iter = deviceManager.queryClassDevices(d1.getEntityClass(), null,
-				VlanVid.ofVlan(1), IPv4Address.of(3), null, null);
+		iter = deviceManager.queryClassDevices(d1.getEntityClass(), MacAddress.NONE,
+				VlanVid.ofVlan(1), IPv4Address.of(3), IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO);
 		count = 0;
 		while (iter.hasNext()) {
 			count += 1;
@@ -2084,8 +2305,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 
 		IDevice d5 = deviceManager.learnDeviceByEntity(entity5);
 		assertEquals(d1.getEntityClass(), d5.getEntityClass());
-		iter = deviceManager.queryClassDevices(d1.getEntityClass(), null,
-				VlanVid.ofVlan(4), IPv4Address.of(3), null, null);
+		iter = deviceManager.queryClassDevices(d1.getEntityClass(), MacAddress.NONE,
+				VlanVid.ofVlan(4), IPv4Address.of(3), IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO);
 		count = 0;
 		Set<Long> deviceKeysFromIterator = new HashSet<Long>();
 		while (iter.hasNext()) {
@@ -2104,7 +2325,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 	public void testDeviceClassIndex() throws Exception {
 		EnumSet<IDeviceService.DeviceField> indexFields =
 				EnumSet.noneOf(IDeviceService.DeviceField.class);
-		indexFields.add(IDeviceService.DeviceField.IPV4);
+		indexFields.add(IDeviceService.DeviceField.IPv4);
 		indexFields.add(IDeviceService.DeviceField.VLAN);
 		deviceManager.addIndex(true, indexFields);
 
@@ -2145,14 +2366,14 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		expect(mockTopology.getL2DomainId(DatapathId.of(EasyMock.anyLong()))).andReturn(DatapathId.of(1L)).anyTimes();
 		replay(mockTopology);
 
-		Entity entity1 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(1), IPv4Address.of(1), DatapathId.of(1L), OFPort.of(1), new Date());
-		Entity entity2 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.of(2), DatapathId.of(1L), OFPort.of(2), new Date());
-		Entity entity2b = new Entity(MacAddress.of(22L), VlanVid.ofVlan(2), IPv4Address.of(2), DatapathId.of(1L), OFPort.of(2), new Date());
+		Entity entity1 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(1), IPv4Address.of(1), IPv6Address.NONE, DatapathId.of(1L), OFPort.of(1), new Date());
+		Entity entity2 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.of(2), IPv6Address.NONE, DatapathId.of(1L), OFPort.of(2), new Date());
+		Entity entity2b = new Entity(MacAddress.of(22L), VlanVid.ofVlan(2), IPv4Address.of(2), IPv6Address.NONE, DatapathId.of(1L), OFPort.of(2), new Date());
 
-		Entity entity3 = new Entity(MacAddress.of(3L), VlanVid.ofVlan(1), IPv4Address.of(3), DatapathId.of(2L), OFPort.of(1), new Date());
-		Entity entity4 = new Entity(MacAddress.of(4L), VlanVid.ofVlan(2), IPv4Address.of(4), DatapathId.of(2L), OFPort.of(2), new Date());
+		Entity entity3 = new Entity(MacAddress.of(3L), VlanVid.ofVlan(1), IPv4Address.of(3), IPv6Address.NONE, DatapathId.of(2L), OFPort.of(1), new Date());
+		Entity entity4 = new Entity(MacAddress.of(4L), VlanVid.ofVlan(2), IPv4Address.of(4), IPv6Address.NONE, DatapathId.of(2L), OFPort.of(2), new Date());
 
-		Entity entity5 = new Entity(MacAddress.of(5L), VlanVid.ofVlan(1), IPv4Address.of(5), DatapathId.of(3L), OFPort.of(1), new Date());
+		Entity entity5 = new Entity(MacAddress.of(5L), VlanVid.ofVlan(1), IPv4Address.of(5), IPv6Address.NONE, DatapathId.of(3L), OFPort.of(1), new Date());
 
 
 		IDevice d1 = deviceManager.learnDeviceByEntity(entity1);
@@ -2174,23 +2395,27 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		assertEquals(d1, deviceManager.findDevice(entity1.getMacAddress(),
 				entity1.getVlan(),
 				entity1.getIpv4Address(),
+				entity1.getIpv6Address(),
 				entity1.getSwitchDPID(),
 				entity1.getSwitchPort()));
 		// port changed. Device will be found through class index
 		assertEquals(d1, deviceManager.findDevice(entity1.getMacAddress(),
 				entity1.getVlan(),
 				entity1.getIpv4Address(),
+				entity1.getIpv6Address(),
 				entity1.getSwitchDPID(),
 				OFPort.of(entity1.getSwitchPort().getPortNumber()+1)));
 		// VLAN changed. No device matches
 		assertEquals(null, deviceManager.findDevice(entity1.getMacAddress(),
 				VlanVid.ofVlan(42),
 				entity1.getIpv4Address(),
+				entity1.getIpv6Address(),
 				entity1.getSwitchDPID(),
 				entity1.getSwitchPort()));
 		assertEquals(null, deviceManager.findDevice(entity1.getMacAddress(),
-				null,
+				VlanVid.ZERO,
 				entity1.getIpv4Address(),
+				entity1.getIpv6Address(),
 				entity1.getSwitchDPID(),
 				entity1.getSwitchPort()));
 		assertEquals(d2, deviceManager.findDeviceByEntity(entity2));
@@ -2198,6 +2423,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		assertEquals(d3, deviceManager.findDevice(entity3.getMacAddress(),
 				entity3.getVlan(),
 				entity3.getIpv4Address(),
+				entity3.getIpv6Address(),
 				entity3.getSwitchDPID(),
 				entity3.getSwitchPort()));
 		// switch and port not set. throws exception
@@ -2206,8 +2432,9 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 			assertEquals(null, deviceManager.findDevice(entity3.getMacAddress(),
 					entity3.getVlan(),
 					entity3.getIpv4Address(),
-					null,
-					null));
+					entity3.getIpv6Address(),
+					DatapathId.NONE,
+					OFPort.ZERO));
 		}
 		catch (IllegalArgumentException e) {
 			exceptionCaught = true;
@@ -2218,6 +2445,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		assertEquals(d5, deviceManager.findDevice(entity5.getMacAddress(),
 				entity5.getVlan(),
 				entity5.getIpv4Address(),
+				entity5.getIpv6Address(),
 				entity5.getSwitchDPID(),
 				entity5.getSwitchPort()));
 		// switch and port not set. throws exception (swith/port are key
@@ -2227,8 +2455,9 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 			assertEquals(d5, deviceManager.findDevice(entity5.getMacAddress(),
 					entity5.getVlan(),
 					entity5.getIpv4Address(),
-					null,
-					null));
+					entity5.getIpv6Address(),
+					DatapathId.NONE,
+					OFPort.ZERO));
 		}
 		catch (IllegalArgumentException e) {
 			exceptionCaught = true;
@@ -2237,7 +2466,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 			fail("findDevice() did not throw IllegalArgumentException");
 
 
-		Entity entityNoClass = new Entity(MacAddress.of(5L), VlanVid.ofVlan(1), IPv4Address.of(5), DatapathId.of(-1L), OFPort.of(1), new Date());
+		Entity entityNoClass = new Entity(MacAddress.of(5L), VlanVid.ofVlan(1), IPv4Address.of(5), IPv6Address.NONE, DatapathId.of(-1L), OFPort.of(1), new Date());
 		assertEquals(null, deviceManager.findDeviceByEntity(entityNoClass));
 
 
@@ -2245,19 +2474,20 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		assertEquals(d1, deviceManager.findClassDevice(d2.getEntityClass(),
 				entity1.getMacAddress(),
 				entity1.getVlan(),
-				entity1.getIpv4Address()));
+				entity1.getIpv4Address(),
+				entity1.getIpv6Address()));
 		assertEquals(d1, deviceManager.findClassDevice(d2.getEntityClass(),
 				entity1.getMacAddress(),
 				entity1.getVlan(),
-				null));
+				IPv4Address.NONE,
+				entity1.getIpv6Address()));
 		assertEquals(null, deviceManager.findClassDevice(d2.getEntityClass(),
 				entity1.getMacAddress(),
-				VlanVid.ofVlan(-1),
-				IPv4Address.of(0)));
+				VlanVid.ZERO,
+				IPv4Address.NONE,
+				entity1.getIpv6Address()));
 	}
 
-
-
 	@Test
 	public void testGetIPv4Addresses() {
 		// Looks like Date is only 1s granularity
@@ -2285,44 +2515,44 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 				andReturn(false).anyTimes();
 		replay(mockTopology);
 
-		Entity e1 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(1), null, null, null, new Date(2000));
+		Entity e1 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(1), IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO, new Date(2000));
 		Device d1 = deviceManager.learnDeviceByEntity(e1);
-		assertArrayEquals(new Integer[0], d1.getIPv4Addresses());
+		assertArrayEquals(new IPv4Address[0], d1.getIPv4Addresses());
 
 
-		Entity e2 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.of(2), null, null, new Date(2000));
+		Entity e2 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.of(2), IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO, new Date(2000));
 		Device d2 = deviceManager.learnDeviceByEntity(e2);
 		d2 = deviceManager.learnDeviceByEntity(e2);
 		assertArrayEquals(new IPv4Address[] { IPv4Address.of(2) }, d2.getIPv4Addresses());
 		// More than one entity
-		Entity e2b = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), null, DatapathId.of(2L), OFPort.of(2), new Date(3000));
+		Entity e2b = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(2L), OFPort.of(2), new Date(3000));
 		d2 = deviceManager.learnDeviceByEntity(e2b);
 		assertEquals(2, d2.entities.length);
 		assertArrayEquals(new IPv4Address[] { IPv4Address.of(2) }, d2.getIPv4Addresses());
 		// and now add an entity with an IP
-		Entity e2c = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.of(2), DatapathId.of(2L), OFPort.of(3), new Date(3000));
+		Entity e2c = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.of(2), IPv6Address.NONE, DatapathId.of(2L), OFPort.of(3), new Date(3000));
 		d2 = deviceManager.learnDeviceByEntity(e2c);
 		assertArrayEquals(new IPv4Address[] { IPv4Address.of(2) }, d2.getIPv4Addresses());
 		assertEquals(3, d2.entities.length);
 
 		// Other devices with different IPs shouldn't interfere
-		Entity e3 = new Entity(MacAddress.of(3L), VlanVid.ofVlan(3), IPv4Address.of(3), null, null, new Date(4000));
-		Entity e3b = new Entity(MacAddress.of(3L), VlanVid.ofVlan(3), IPv4Address.of(3), DatapathId.of(3L), OFPort.of(3), new Date(4400));
+		Entity e3 = new Entity(MacAddress.of(3L), VlanVid.ofVlan(3), IPv4Address.of(3), IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO, new Date(4000));
+		Entity e3b = new Entity(MacAddress.of(3L), VlanVid.ofVlan(3), IPv4Address.of(3), IPv6Address.NONE, DatapathId.of(3L), OFPort.of(3), new Date(4400));
 		Device d3 = deviceManager.learnDeviceByEntity(e3);
 		d3 = deviceManager.learnDeviceByEntity(e3b);
 		assertArrayEquals(new IPv4Address[] { IPv4Address.of(2) }, d2.getIPv4Addresses());
 		assertArrayEquals(new IPv4Address[] { IPv4Address.of(3) }, d3.getIPv4Addresses());
 
 		// Add another IP to d3
-		Entity e3c = new Entity(MacAddress.of(3L), VlanVid.ofVlan(3), IPv4Address.of(33), DatapathId.of(3L), OFPort.of(3), new Date(4400));
+		Entity e3c = new Entity(MacAddress.of(3L), VlanVid.ofVlan(3), IPv4Address.of(33), IPv6Address.NONE, DatapathId.of(3L), OFPort.of(3), new Date(4400));
 		d3 = deviceManager.learnDeviceByEntity(e3c);
 		IPv4Address[] ips = d3.getIPv4Addresses();
 		Arrays.sort(ips);
 		assertArrayEquals(new IPv4Address[] { IPv4Address.of(3), IPv4Address.of(33) }, ips);
 
 		// Add another device that also claims IP2 but is older than e2
-		Entity e4 = new Entity(MacAddress.of(4L), VlanVid.ofVlan(4), IPv4Address.of(2), null, null, new Date(1000));
-		Entity e4b = new Entity(MacAddress.of(4L), VlanVid.ofVlan(4), null, DatapathId.of(4L), OFPort.of(4), new Date(1000));
+		Entity e4 = new Entity(MacAddress.of(4L), VlanVid.ofVlan(4), IPv4Address.of(2), IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO, new Date(1000));
+		Entity e4b = new Entity(MacAddress.of(4L), VlanVid.ofVlan(4), IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(4L), OFPort.of(4), new Date(1000));
 		Device d4 = deviceManager.learnDeviceByEntity(e4);
 		assertArrayEquals(new IPv4Address[] { IPv4Address.of(2) }, d2.getIPv4Addresses());
 		assertArrayEquals(new IPv4Address[0],  d4.getIPv4Addresses());
@@ -2331,53 +2561,160 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		assertArrayEquals(new IPv4Address[0], d4.getIPv4Addresses());
 
 		// Make e4 and e4a newer
-		Entity e4c = new Entity(MacAddress.of(4L), VlanVid.ofVlan(4), IPv4Address.of(2), null, null, new Date(5000));
-		Entity e4d = new Entity(MacAddress.of(4L), VlanVid.ofVlan(4), null, DatapathId.of(4L), OFPort.of(5), new Date(5000));
+		Entity e4c = new Entity(MacAddress.of(4L), VlanVid.ofVlan(4), IPv4Address.of(2), IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO, new Date(5000));
+		Entity e4d = new Entity(MacAddress.of(4L), VlanVid.ofVlan(4), IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(4L), OFPort.of(5), new Date(5000));
 		d4 = deviceManager.learnDeviceByEntity(e4c);
 		d4 = deviceManager.learnDeviceByEntity(e4d);
 		assertArrayEquals(new IPv4Address[0], d2.getIPv4Addresses());
-		// FIXME: d4 should not return IP4
 		assertArrayEquals(new IPv4Address[] { IPv4Address.of(2) }, d4.getIPv4Addresses());
 
 		// Add another newer entity to d2 but with different IP
-		Entity e2d = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.of(22), DatapathId.of(4L), OFPort.of(6), new Date(6000));
+		Entity e2d = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.of(22), IPv6Address.NONE, DatapathId.of(4L), OFPort.of(6), new Date(6000));
 		d2 = deviceManager.learnDeviceByEntity(e2d);
 		assertArrayEquals(new IPv4Address[] { IPv4Address.of(22) }, d2.getIPv4Addresses());
 		assertArrayEquals(new IPv4Address[] { IPv4Address.of(2) }, d4.getIPv4Addresses());
 
 		// new IP for d2,d4 but with same timestamp. Both devices get the IP
-		Entity e2e = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.of(42), DatapathId.of(2L), OFPort.of(4), new Date(7000));
+		Entity e2e = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.of(42), IPv6Address.NONE, DatapathId.of(2L), OFPort.of(4), new Date(7000));
 		d2 = deviceManager.learnDeviceByEntity(e2e);
 		ips= d2.getIPv4Addresses();
 		Arrays.sort(ips);
 		assertArrayEquals(new IPv4Address[] { IPv4Address.of(22), IPv4Address.of(42) }, ips);
-		Entity e4e = new Entity(MacAddress.of(4L), VlanVid.ofVlan(4), IPv4Address.of(42), DatapathId.of(4L), OFPort.of(7), new Date(7000));
+		Entity e4e = new Entity(MacAddress.of(4L), VlanVid.ofVlan(4), IPv4Address.of(42), IPv6Address.NONE, DatapathId.of(4L), OFPort.of(7), new Date(7000));
 		d4 = deviceManager.learnDeviceByEntity(e4e);
 		ips= d4.getIPv4Addresses();
 		Arrays.sort(ips);
 		assertArrayEquals(new IPv4Address[] { IPv4Address.of(2), IPv4Address.of(42) }, ips);
 
 		// add a couple more IPs
-		Entity e2f = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.of(4242), DatapathId.of(2L), OFPort.of(5), new Date(8000));
+		Entity e2f = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.of(4242), IPv6Address.NONE, DatapathId.of(2L), OFPort.of(5), new Date(8000));
 		d2 = deviceManager.learnDeviceByEntity(e2f);
 		ips= d2.getIPv4Addresses();
 		Arrays.sort(ips);
 		assertArrayEquals(new IPv4Address[] { IPv4Address.of(22), IPv4Address.of(42), IPv4Address.of(4242) }, ips);
-		Entity e4f = new Entity(MacAddress.of(4L), VlanVid.ofVlan(4), IPv4Address.of(4242), DatapathId.of(4L), OFPort.of(8), new Date(9000));
+		Entity e4f = new Entity(MacAddress.of(4L), VlanVid.ofVlan(4), IPv4Address.of(4242), IPv6Address.NONE, DatapathId.of(4L), OFPort.of(8), new Date(9000));
 		d4 = deviceManager.learnDeviceByEntity(e4f);
 		ips= d4.getIPv4Addresses();
 		Arrays.sort(ips);
 		assertArrayEquals(new IPv4Address[] { IPv4Address.of(2), IPv4Address.of(42), IPv4Address.of(4242) }, ips);
 	}
+	
+	@Test
+	public void testGetIPv6Addresses() {
+		// Looks like Date is only 1s granularity
+
+		ITopologyService mockTopology = createMock(ITopologyService.class);
+		deviceManager.topology = mockTopology;
+		expect(mockTopology.isAttachmentPointPort(DatapathId.of(anyLong()),
+				OFPort.of(anyShort()))).
+				andReturn(true).anyTimes();
+		expect(mockTopology.getL2DomainId(DatapathId.of(anyLong()))).andReturn(DatapathId.of(1L)).anyTimes();
+		expect(mockTopology.isConsistent(DatapathId.of(EasyMock.anyLong()),
+				OFPort.of(EasyMock.anyShort()),
+				DatapathId.of(EasyMock.anyLong()),
+				OFPort.of(EasyMock.anyShort())))
+				.andReturn(false)
+				.anyTimes();
+		expect(mockTopology.isBroadcastDomainPort(DatapathId.of(EasyMock.anyLong()),
+				OFPort.of(EasyMock.anyShort())))
+				.andReturn(false)
+				.anyTimes();
+		expect(mockTopology.isInSameBroadcastDomain(DatapathId.of(EasyMock.anyLong()),
+				OFPort.of(EasyMock.anyShort()),
+				DatapathId.of(EasyMock.anyLong()),
+				OFPort.of(EasyMock.anyShort()))).
+				andReturn(false).anyTimes();
+		replay(mockTopology);
+
+		Entity e1 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(1), IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO, new Date(2000));
+		Device d1 = deviceManager.learnDeviceByEntity(e1);
+		assertArrayEquals(new IPv6Address[0], d1.getIPv6Addresses());
+
+		Entity e2 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.NONE, IPv6Address.of(2, 2), DatapathId.NONE, OFPort.ZERO, new Date(2000));
+		Device d2 = deviceManager.learnDeviceByEntity(e2);
+		d2 = deviceManager.learnDeviceByEntity(e2);
+		assertArrayEquals(new IPv6Address[] { IPv6Address.of(2, 2) }, d2.getIPv6Addresses());
+		// More than one entity
+		Entity e2b = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(2L), OFPort.of(2), new Date(3000));
+		d2 = deviceManager.learnDeviceByEntity(e2b);
+		assertEquals(2, d2.entities.length);
+		assertArrayEquals(new IPv6Address[] { IPv6Address.of(2, 2) }, d2.getIPv6Addresses());
+		// and now add an entity with an IP
+		Entity e2c = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.NONE, IPv6Address.of(2, 2), DatapathId.of(2L), OFPort.of(3), new Date(3000));
+		d2 = deviceManager.learnDeviceByEntity(e2c);
+		assertArrayEquals(new IPv6Address[] { IPv6Address.of(2, 2) }, d2.getIPv6Addresses());
+		assertEquals(3, d2.entities.length);
+
+		// Other devices with different IPs shouldn't interfere
+		Entity e3 = new Entity(MacAddress.of(3L), VlanVid.ofVlan(3), IPv4Address.NONE, IPv6Address.of(3, 3), DatapathId.NONE, OFPort.ZERO, new Date(4000));
+		Entity e3b = new Entity(MacAddress.of(3L), VlanVid.ofVlan(3), IPv4Address.NONE, IPv6Address.of(3, 3), DatapathId.of(3L), OFPort.of(3), new Date(4400));
+		Device d3 = deviceManager.learnDeviceByEntity(e3);
+		d3 = deviceManager.learnDeviceByEntity(e3b);
+		assertArrayEquals(new IPv6Address[] { IPv6Address.of(2, 2) }, d2.getIPv6Addresses());
+		assertArrayEquals(new IPv6Address[] { IPv6Address.of(3, 3) }, d3.getIPv6Addresses());
+
+		// Add another IP to d3
+		Entity e3c = new Entity(MacAddress.of(3L), VlanVid.ofVlan(3), IPv4Address.NONE, IPv6Address.of(33, 33), DatapathId.of(3L), OFPort.of(3), new Date(4400));
+		d3 = deviceManager.learnDeviceByEntity(e3c);
+		IPv6Address[] ips = d3.getIPv6Addresses();
+		Arrays.sort(ips);
+		assertArrayEquals(new IPv6Address[] { IPv6Address.of(3, 3), IPv6Address.of(33, 33) }, ips);
+
+		// Add another device that also claims IP2 but is older than e2
+		Entity e4 = new Entity(MacAddress.of(4L), VlanVid.ofVlan(4), IPv4Address.NONE, IPv6Address.of(2, 2), DatapathId.NONE, OFPort.ZERO, new Date(1000));
+		Entity e4b = new Entity(MacAddress.of(4L), VlanVid.ofVlan(4), IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(4L), OFPort.of(4), new Date(1000));
+		Device d4 = deviceManager.learnDeviceByEntity(e4);
+		assertArrayEquals(new IPv6Address[] { IPv6Address.of(2, 2) }, d2.getIPv6Addresses());
+		assertArrayEquals(new IPv6Address[0],  d4.getIPv6Addresses());
+		// add another entity to d4
+		d4 = deviceManager.learnDeviceByEntity(e4b);
+		assertArrayEquals(new IPv6Address[0], d4.getIPv6Addresses());
+
+		// Make e4 and e4a newer
+		Entity e4c = new Entity(MacAddress.of(4L), VlanVid.ofVlan(4), IPv4Address.NONE, IPv6Address.of(2, 2), DatapathId.NONE, OFPort.ZERO, new Date(5000));
+		Entity e4d = new Entity(MacAddress.of(4L), VlanVid.ofVlan(4), IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(4L), OFPort.of(5), new Date(5000));
+		d4 = deviceManager.learnDeviceByEntity(e4c);
+		d4 = deviceManager.learnDeviceByEntity(e4d);
+		assertArrayEquals(new IPv6Address[0], d2.getIPv6Addresses());
+		assertArrayEquals(new IPv6Address[] { IPv6Address.of(2, 2) }, d4.getIPv6Addresses());
+
+		// Add another newer entity to d2 but with different IP
+		Entity e2d = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.NONE, IPv6Address.of(22, 22), DatapathId.of(4L), OFPort.of(6), new Date(6000));
+		d2 = deviceManager.learnDeviceByEntity(e2d);
+		assertArrayEquals(new IPv6Address[] { IPv6Address.of(22, 22) }, d2.getIPv6Addresses());
+		assertArrayEquals(new IPv6Address[] { IPv6Address.of(2, 2) }, d4.getIPv6Addresses());
+
+		// new IP for d2,d4 but with same timestamp. Both devices get the IP
+		Entity e2e = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.NONE, IPv6Address.of(42, 42), DatapathId.of(2L), OFPort.of(4), new Date(7000));
+		d2 = deviceManager.learnDeviceByEntity(e2e);
+		ips= d2.getIPv6Addresses();
+		Arrays.sort(ips);
+		assertArrayEquals(new IPv6Address[] { IPv6Address.of(22, 22), IPv6Address.of(42, 42) }, ips);
+		Entity e4e = new Entity(MacAddress.of(4L), VlanVid.ofVlan(4), IPv4Address.NONE, IPv6Address.of(42, 42), DatapathId.of(4L), OFPort.of(7), new Date(7000));
+		d4 = deviceManager.learnDeviceByEntity(e4e);
+		ips= d4.getIPv6Addresses();
+		Arrays.sort(ips);
+		assertArrayEquals(new IPv6Address[] { IPv6Address.of(2, 2), IPv6Address.of(42, 42) }, ips);
+
+		// add a couple more IPs
+		Entity e2f = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.NONE, IPv6Address.of(4242, 4242), DatapathId.of(2L), OFPort.of(5), new Date(8000));
+		d2 = deviceManager.learnDeviceByEntity(e2f);
+		ips= d2.getIPv6Addresses();
+		Arrays.sort(ips);
+		assertArrayEquals(new IPv6Address[] { IPv6Address.of(22, 22), IPv6Address.of(42, 42), IPv6Address.of(4242, 4242) }, ips);
+		Entity e4f = new Entity(MacAddress.of(4L), VlanVid.ofVlan(4), IPv4Address.NONE, IPv6Address.of(4242, 4242), DatapathId.of(4L), OFPort.of(8), new Date(9000));
+		d4 = deviceManager.learnDeviceByEntity(e4f);
+		ips= d4.getIPv6Addresses();
+		Arrays.sort(ips);
+		assertArrayEquals(new IPv6Address[] { IPv6Address.of(2, 2), IPv6Address.of(42, 42), IPv6Address.of(4242, 4242) }, ips);
+	}
 
-	// TODO: this test should really go into a separate class that collects
-	// unit tests for Device
 	@Test
 	public void testGetSwitchPortVlanId() {
-		Entity entity1 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(1), null, DatapathId.of(10L), OFPort.of(1), new Date());
-		Entity entity2 = new Entity(MacAddress.of(1L), null, null, DatapathId.of(10L), OFPort.of(1), new Date());
-		Entity entity3 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(3), null,  DatapathId.of(1L), OFPort.of(1), new Date());
-		Entity entity4 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(42), null,  DatapathId.of(1L), OFPort.of(1), new Date());
+		Entity entity1 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(1), IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(10L), OFPort.of(1), new Date());
+		Entity entity2 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(10L), OFPort.of(1), new Date());
+		Entity entity3 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(3), IPv4Address.NONE, IPv6Address.NONE,  DatapathId.of(1L), OFPort.of(1), new Date());
+		Entity entity4 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(42), IPv4Address.NONE, IPv6Address.NONE,  DatapathId.of(1L), OFPort.of(1), new Date());
 		Entity[] entities = new Entity[] { entity1, entity2,
 				entity3, entity4
 		};
@@ -2387,7 +2724,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		SwitchPort swp1x2 = new SwitchPort(DatapathId.of(1L), OFPort.of(2));
 		SwitchPort swp2x1 = new SwitchPort(DatapathId.of(2L), OFPort.of(1));
 		SwitchPort swp10x1 = new SwitchPort(DatapathId.of(10L), OFPort.of(1));
-		assertArrayEquals(new VlanVid[] { VlanVid.ofVlan(-1), VlanVid.ofVlan(1)},
+		assertArrayEquals(new VlanVid[] { VlanVid.ZERO, VlanVid.ofVlan(1)},
 				d.getSwitchPortVlanIds(swp10x1));
 		assertArrayEquals(new VlanVid[] { VlanVid.ofVlan(3), VlanVid.ofVlan(42)},
 				d.getSwitchPortVlanIds(swp1x1));
@@ -2424,10 +2761,10 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 
 		//flexClassifier.createTestEntityClass("Class1");
 
-		Entity entity1 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(1), IPv4Address.of(1), DatapathId.of(1L), OFPort.of(1), new Date());
-		Entity entity1b = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.of(1), DatapathId.of(1L), OFPort.of(1), new Date());
-		Entity entity2 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(1), IPv4Address.of(2), DatapathId.of(2L), OFPort.of(2), new Date());
-		Entity entity2b = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.of(2), DatapathId.of(2L), OFPort.of(2), new Date());
+		Entity entity1 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(1), IPv4Address.of(1), IPv6Address.NONE, DatapathId.of(1L), OFPort.of(1), new Date());
+		Entity entity1b = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.of(1), IPv6Address.NONE, DatapathId.of(1L), OFPort.of(1), new Date());
+		Entity entity2 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(1), IPv4Address.of(2), IPv6Address.NONE, DatapathId.of(2L), OFPort.of(2), new Date());
+		Entity entity2b = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.of(2), IPv6Address.NONE, DatapathId.of(2L), OFPort.of(2), new Date());
 
 
 		Device d1 = deviceManager.learnDeviceByEntity(entity1);
@@ -2436,20 +2773,20 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		Device d2b = deviceManager.learnDeviceByEntity(entity2b);
 
 		d1 = deviceManager.getDeviceIteratorForQuery(entity1.getMacAddress(),
-				entity1.getVlan(), entity1.getIpv4Address(),
+				entity1.getVlan(), entity1.getIpv4Address(), entity1.getIpv6Address(),
 				entity1.getSwitchDPID(), entity1.getSwitchPort())
 				.next();
 		d1b = deviceManager.getDeviceIteratorForQuery(entity1b.getMacAddress(),
-				entity1b.getVlan(), entity1b.getIpv4Address(),
+				entity1b.getVlan(), entity1b.getIpv4Address(), entity1b.getIpv6Address(),
 				entity1b.getSwitchDPID(), entity1b.getSwitchPort()).next();
 
 		assertEquals(d1, d1b);
 
 		d2 = deviceManager.getDeviceIteratorForQuery(entity2.getMacAddress(),
-				entity2.getVlan(), entity2.getIpv4Address(),
+				entity2.getVlan(), entity2.getIpv4Address(), entity2.getIpv6Address(),
 				entity2.getSwitchDPID(), entity2.getSwitchPort()).next();
 		d2b = deviceManager.getDeviceIteratorForQuery(entity2b.getMacAddress(),
-				entity2b.getVlan(), entity2b.getIpv4Address(),
+				entity2b.getVlan(), entity2b.getIpv4Address(), entity2b.getIpv6Address(),
 				entity2b.getSwitchDPID(), entity2b.getSwitchPort()).next();
 		assertEquals(d2, d2b);
 
@@ -2520,8 +2857,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 	@Test
 	public void testSyncEntity() {
 		Date d1 = new Date();
-		Date d2 = new Date(0);
-		Entity e1 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.of(3), DatapathId.of(4L), OFPort.of(5), d1);
+		Date d2 = new Date(1);
+		Entity e1 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.of(3), IPv6Address.NONE, DatapathId.of(4L), OFPort.of(5), d1);
 		e1.setActiveSince(d2);
 		SyncEntity se1 = new SyncEntity(e1);
 		assertEntityEquals(e1, se1);
@@ -2535,7 +2872,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		assertNotSame(d1, se1.lastSeenTimestamp);
 		assertNotSame(d2, se1.activeSince);
 
-		Entity e2 = new Entity(MacAddress.of(42L), null, null, null, null, null);
+		Entity e2 = new Entity(MacAddress.of(42L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO, Entity.NO_DATE);
 		SyncEntity se2 = new SyncEntity(e2);
 		assertEntityEquals(e2, se2);
 
@@ -2564,8 +2901,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		assertEquals("MyKey", dsr.toString());
 
 		List<SyncEntity> entities = new ArrayList<SyncEntity>();
-		Entity e1a = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.of(3), DatapathId.of(4L), OFPort.of(5), new Date(1000));
-		Entity e1b = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), null, DatapathId.of(4L), OFPort.of(5), new Date(0));
+		Entity e1a = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.of(3), IPv6Address.NONE, DatapathId.of(4L), OFPort.of(5), new Date(1000));
+		Entity e1b = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(4L), OFPort.of(5), new Date(1));
 		entities.add(new SyncEntity(e1a));
 		entities.add(new SyncEntity(e1b));
 		// e1b comes before e1 (lastSeen) but we add it after it to test
@@ -2593,7 +2930,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 
 		//**************************************
 		// Test 1: a single entity
-		Entity e1 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.of(3), DatapathId.of(4L), OFPort.of(5), new Date(1000));
+		Entity e1 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.of(3), IPv6Address.NONE, DatapathId.of(4L), OFPort.of(5), new Date(1000));
 		Device d1 = deviceManager.learnDeviceByEntity(e1);
 		assertEquals("Sanity check failed. Device doesn't have the expected " +
 				"entity class. Something with the test setup is strange",
@@ -2614,7 +2951,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		// Test 1b: same device, now with a second entity (no IP).
 		// this second entity has a lastSeen time that is earlier than the
 		// first entity
-		Entity e1b = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), null, DatapathId.of(4L), OFPort.of(5), new Date(0));
+		Entity e1b = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(4L), OFPort.of(5), new Date(1));
 		d1 = deviceManager.learnDeviceByEntity(e1b);
 		assertEquals("Sanity check failed. Should still be same device but " +
 				"deviceKeys differs", deviceKey, d1.getDeviceKey());
@@ -2630,7 +2967,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		//**************************************
 		// Test 1c: same device with a third entity that does not have a
 		// switch port. It should be added to the DeviceSyncRepresentation
-		Entity e1c = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.of(33), null, null, new Date(2000));
+		Entity e1c = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.of(33), IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO, new Date(2000));
 		d1 = deviceManager.learnDeviceByEntity(e1c);
 		assertEquals("Sanity check failed. Should still be same device but " +
 				"deviceKeys differs", deviceKey, d1.getDeviceKey());
@@ -2648,7 +2985,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		// attachment point and that is newer. Device should move and
 		// non-attachment point entities should be removed (e1b). Although
 		// e1 is non-attachment point it will remain because it has an IP
-		Entity e1d = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.of(33), DatapathId.of(4L), OFPort.of(6), new Date(3000));
+		Entity e1d = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.of(33), IPv6Address.NONE, DatapathId.of(4L), OFPort.of(6), new Date(3000));
 		d1 = deviceManager.learnDeviceByEntity(e1d);
 		assertEquals("Sanity check failed. Should still be same device but " +
 				"deviceKeys differs", deviceKey, d1.getDeviceKey());
@@ -2662,12 +2999,11 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 
 		d1 = null;
 
-
 		//**************************************
 		// Test 2: a second device with a different entity class. The
 		// mock entity classifier will return an entity class where all
 		// fields are keys if the DPID is > 10L
-		Entity e2 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(23), IPv4Address.of(24), DatapathId.of(11L), OFPort.of(1), new Date(0));
+		Entity e2 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(23), IPv4Address.of(24), IPv6Address.NONE, DatapathId.of(11L), OFPort.of(1), new Date(1));
 		Device d2 = deviceManager.learnDeviceByEntity(e2);
 		DeviceSyncRepresentation dsr2 = new DeviceSyncRepresentation(d2);
 		assertEquals("Sanity check failed. Device doesn't have the expected " +
@@ -2740,8 +3076,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		deviceManager.topology = mockTopology;
 		deviceManager.setSyncStoreWriteInterval(syncStoreIntervalMs);
 
-		Entity e1a = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.of(3), DatapathId.of(4L), OFPort.of(5), new Date(1000));
-		e1a.setActiveSince(new Date(0));
+		Entity e1a = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.of(3), IPv6Address.NONE, DatapathId.of(4L), OFPort.of(5), new Date(1000));
+		e1a.setActiveSince(new Date(1));
 		deviceManager.learnDeviceByEntity(e1a);
 
 		//storeClient.put("FooBar", new DeviceSyncRepresentation());
@@ -2754,8 +3090,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 
 		// Same entity but newer timestamp. Since the device hasn't changed,
 		// only the timestamp is updated and the write should be throttled.
-		Entity e1b = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.of(3), DatapathId.of(4L), OFPort.of(5), new Date(2000));
-		e1b.setActiveSince(new Date(0));
+		Entity e1b = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.of(3), IPv6Address.NONE, DatapathId.of(4L), OFPort.of(5), new Date(2000));
+		e1b.setActiveSince(new Date(1)); /* cannot use Date(0), since that's our 'no date' value now */
 		deviceManager.learnDeviceByEntity(e1a);
 		entries = getEntriesFromStore();
 		assertEquals(1, entries.size());
@@ -2765,8 +3101,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 
 		// Wait for the write interval to expire then write again.
 		Thread.sleep(syncStoreIntervalMs+5);
-		Entity e1c = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.of(3), DatapathId.of(4L), OFPort.of(5), new Date(3000));
-		e1c.setActiveSince(new Date(0));
+		Entity e1c = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.of(3), IPv6Address.NONE, DatapathId.of(4L), OFPort.of(5), new Date(3000));
+		e1c.setActiveSince(new Date(1));
 		deviceManager.learnDeviceByEntity(e1c);
 		entries = getEntriesFromStore();
 		assertEquals(1, entries.size());
@@ -2776,8 +3112,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 
 		// Entity for same device but with different IP. should be added
 		// immediately
-		Entity e1d = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.of(33), DatapathId.of(4L), OFPort.of(5), new Date(4000));
-		e1d.setActiveSince(new Date(0));
+		Entity e1d = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.of(33), IPv6Address.NONE, DatapathId.of(4L), OFPort.of(5), new Date(4000));
+		e1d.setActiveSince(new Date(1));
 		deviceManager.learnDeviceByEntity(e1d);
 		entries = getEntriesFromStore();
 		assertEquals(1, entries.size());
@@ -2790,8 +3126,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		// update immediately without throttle.
 		// Note: the previous entities will still be there because they have
 		// IPs (even though they aren't for the current attachment point)
-		Entity e1e = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.of(33), DatapathId.of(4L), OFPort.of(6), new Date(5000));
-		e1e.setActiveSince(new Date(0));
+		Entity e1e = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.of(33), IPv6Address.NONE, DatapathId.of(4L), OFPort.of(6), new Date(5000));
+		e1e.setActiveSince(new Date(1));
 		deviceManager.learnDeviceByEntity(e1e);
 		entries = getEntriesFromStore();
 		assertEquals(1, entries.size());
@@ -2802,7 +3138,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		assertEntityEquals(e1e, dsr1.getEntities().get(2));
 
 		// Add a second device
-		Entity e2 = new Entity(MacAddress.of(2L), null, null, DatapathId.of(5L), OFPort.of(5), new Date());
+		Entity e2 = new Entity(MacAddress.of(2L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(5L), OFPort.of(5), new Date());
 		deviceManager.learnDeviceByEntity(e2);
 		entries = getEntriesFromStore();
 		assertEquals(2, entries.size());
@@ -2852,7 +3188,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 
 	private IDevice getSingleDeviceFromDeviceManager(long mac) {
 		Iterator<? extends IDevice> diter =
-				deviceManager.queryDevices(MacAddress.of(mac), null, null, null, null);
+				deviceManager.queryDevices(MacAddress.of(mac), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO);
 		assertTrue("Query didn't return a device", diter.hasNext());
 		IDevice d = diter.next();
 		assertFalse("Query returned more than one device", diter.hasNext());
@@ -2872,8 +3208,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		deviceManager.setInitialSyncStoreConsolidateMs(initialSyncStoreConsolidateIntervalMs);
 
 		// Add Device1 with two entities with two different IPs
-		Entity e1a = new Entity(MacAddress.of(1L), null, IPv4Address.of(3), DatapathId.of(4L), OFPort.of(5), new Date(1000));
-		Entity e1b = new Entity(MacAddress.of(1L), null, IPv4Address.of(33),  DatapathId.of(4L), OFPort.of(5), new Date(2000));
+		Entity e1a = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.of(3), IPv6Address.NONE, DatapathId.of(4L), OFPort.of(5), new Date(1000));
+		Entity e1b = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.of(33), IPv6Address.NONE, DatapathId.of(4L), OFPort.of(5), new Date(2000));
 		Device d1 = deviceManager.allocateDevice(1L, e1a,
 				DefaultEntityClassifier.entityClass);
 		d1 = deviceManager.allocateDevice(d1, e1b, -1);
@@ -2882,8 +3218,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 
 		// Add Device2 with different switch-ports. Only the most recent
 		// one should be the attachment point
-		Entity e2a = new Entity(MacAddress.of(2L), null, null, DatapathId.of(4L), OFPort.of(4), new Date(1000));
-		Entity e2b = new Entity(MacAddress.of(2L), null, null, DatapathId.of(4L), OFPort.of(5), new Date(2000));
+		Entity e2a = new Entity(MacAddress.of(2L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(4L), OFPort.of(4), new Date(1000));
+		Entity e2b = new Entity(MacAddress.of(2L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(4L), OFPort.of(5), new Date(2000));
 		Device d2 = deviceManager.allocateDevice(2L, e2a,
 				DefaultEntityClassifier.entityClass);
 		d2 = deviceManager.allocateDevice(d2, e2b, -1);
@@ -2909,15 +3245,15 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		// Query for the Device1. Make sure we have the two IPs we stored.
 		IDevice d = getSingleDeviceFromDeviceManager(1L);
 		assertDeviceIps(new IPv4Address[] {IPv4Address.of(3), IPv4Address.of(33)}, d);
-		assertArrayEquals(new VlanVid[] { VlanVid.ofVlan(-1) }, d.getVlanId());
+		assertArrayEquals(new VlanVid[] { VlanVid.ZERO }, d.getVlanId());
 		swp = new SwitchPort(DatapathId.of(4L), OFPort.of(5));
 		assertArrayEquals(new SwitchPort[] { swp }, d.getAttachmentPoints());
 
 		// Query for Device2. Make sure we only have the more recent AP
 		// Query for the Device1. Make sure we have the two IPs we stored.
 		d = getSingleDeviceFromDeviceManager(2L);
-		assertArrayEquals(new Integer[0], d.getIPv4Addresses());
-		assertArrayEquals(new VlanVid[] { VlanVid.ofVlan(-1) }, d.getVlanId());
+		assertArrayEquals(new IPv4Address[0], d.getIPv4Addresses());
+		assertArrayEquals(new VlanVid[] { VlanVid.ZERO }, d.getVlanId());
 		swp = new SwitchPort(DatapathId.of(4L), OFPort.of(5));
 		assertArrayEquals(new SwitchPort[] { swp }, d.getAttachmentPoints());
 
@@ -2925,7 +3261,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		// add another entry device to the store. since device manager is
 		// already master we won't read this device and it should be
 		// removed from the store by the consolidate task
-		Entity e3 = new Entity(MacAddress.of(3L), null, null, DatapathId.of(1L), OFPort.of(1), null);
+		Entity e3 = new Entity(MacAddress.of(3L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(1L), OFPort.of(1), Entity.NO_DATE);
 		dsr = new DeviceSyncRepresentation();
 		dsr.setKey("Device3");
 		dsr.setEntities(Collections.singletonList(new SyncEntity(e3)));
@@ -2941,7 +3277,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		assertTrue("Device3 not in store. Entries in store: " + entries, found);
 		// make sure it's not in DevManager
 		Iterator<? extends IDevice> diter =
-				deviceManager.queryDevices(MacAddress.of(3L), null, null, null, null);
+				deviceManager.queryDevices(MacAddress.of(3L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO);
 		assertFalse("Device3 found in DeviceManager. Should be there",
 				diter.hasNext());
 
@@ -2957,7 +3293,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		assertFalse("Device3 not is still in the store. Entries in store: "
 				+ entries, found);
 		// make sure it's not in DevManager
-		diter = deviceManager.queryDevices(MacAddress.of(3L), null, null, null, null);
+		diter = deviceManager.queryDevices(MacAddress.of(3L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO);
 		assertFalse("Device3 found in DeviceManager. Should be there",
 				diter.hasNext());
 	}
@@ -2975,15 +3311,15 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 
 		// Add Device1 with two entities to store and let device manager
 		// learn
-		Entity e1a = new Entity(MacAddress.of(1L), null, null, DatapathId.of(4L), OFPort.of(5), new Date(1000));
-		Entity e1b = new Entity(MacAddress.of(1L), null, IPv4Address.of(3),  DatapathId.of(4L), OFPort.of(5), new Date(2000));
+		Entity e1a = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(4L), OFPort.of(5), new Date(1000));
+		Entity e1b = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.of(3), IPv6Address.NONE, DatapathId.of(4L), OFPort.of(5), new Date(2000));
 		Device d1 = deviceManager.learnDeviceByEntity(e1a);
 		deviceManager.learnDeviceByEntity(e1b);
 		String dev1Key = DeviceSyncRepresentation.computeKey(d1);
 
 
 		// Add a second device to the store but do NOT add to device manager
-		Entity e2 = new Entity(MacAddress.of(2L), null, null, DatapathId.of(5L), OFPort.of(5), new Date());
+		Entity e2 = new Entity(MacAddress.of(2L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(5L), OFPort.of(5), new Date());
 		Device d2 = deviceManager.allocateDevice(42L, e2,
 				DefaultEntityClassifier.entityClass);
 		DeviceSyncRepresentation dsr = new DeviceSyncRepresentation(d2);
@@ -3022,7 +3358,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 
 		// Now write a device to the store that doesn't have any switch-port
 		// it should be removed
-		Entity e3 = new Entity(MacAddress.of(3L), null, null, null, null, null);
+		Entity e3 = new Entity(MacAddress.of(3L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO, Entity.NO_DATE);
 		dsr.setKey("Device3");
 		dsr.setEntities(Collections.singletonList(new SyncEntity(e3)));
 		storeClient.put(dsr.getKey(), dsr);
@@ -3033,7 +3369,5 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		Thread.sleep(25); // give the scheduler time to run the task
 		versioned = storeClient.get("Device3");
 		assertNull(versioned.getValue());
-
 	}
-
-}
+}
\ No newline at end of file
diff --git a/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceUniqueIndexTest.java b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceUniqueIndexTest.java
index e820f9e082e1ce9aa4c0686f7b87f05c34d88897..47590faaaca6c85e9f0086a3ea491920cc1cbdb1 100644
--- a/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceUniqueIndexTest.java
+++ b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceUniqueIndexTest.java
@@ -28,6 +28,7 @@ import java.util.Iterator;
 import org.junit.Test;
 import org.projectfloodlight.openflow.types.DatapathId;
 import org.projectfloodlight.openflow.types.IPv4Address;
+import org.projectfloodlight.openflow.types.IPv6Address;
 import org.projectfloodlight.openflow.types.MacAddress;
 import org.projectfloodlight.openflow.types.OFPort;
 import org.projectfloodlight.openflow.types.VlanVid;
@@ -52,8 +53,8 @@ public class DeviceUniqueIndexTest extends TestCase {
     @Override
     protected void setUp() throws Exception {
         super.setUp();
-        e1a = new Entity(MacAddress.of(1L), VlanVid.ofVlan(1), IPv4Address.of(1), DatapathId.of(1L), OFPort.of(1), new Date());
-        e1b = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.of(1), DatapathId.of(1L), OFPort.of(1), new Date());
+        e1a = new Entity(MacAddress.of(1L), VlanVid.ofVlan(1), IPv4Address.of(1), IPv6Address.of(1, 1), DatapathId.of(1L), OFPort.of(1), new Date());
+        e1b = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.of(1), IPv6Address.of(1, 1), DatapathId.of(1L), OFPort.of(1), new Date());
         List<Entity> d1Entities = new ArrayList<Entity>(2);
         d1Entities.add(e1a);
         d1Entities.add(e1b);
@@ -61,14 +62,14 @@ public class DeviceUniqueIndexTest extends TestCase {
                         d1Entities, null);
         
         // e2 and e2 alt match in MAC and VLAN
-        e2 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.of(2), DatapathId.of(2L), OFPort.of(2), new Date());
-        e2alt = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), null, null, null, null);
+        e2 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.of(2), IPv6Address.of(2, 2), DatapathId.of(2L), OFPort.of(2), new Date());
+        e2alt = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO, Entity.NO_DATE);
         
         // IP is null
-        e3 = new Entity(MacAddress.of(3L), VlanVid.ofVlan(3), null, DatapathId.of(3L), OFPort.of(3), new Date());
+        e3 = new Entity(MacAddress.of(3L), VlanVid.ofVlan(3), IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(3L), OFPort.of(3), new Date());
         
         // IP and switch and port are null
-        e4 = new Entity(MacAddress.of(4L), VlanVid.ofVlan(4), null, null, null, new Date());
+        e4 = new Entity(MacAddress.of(4L), VlanVid.ofVlan(4), IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO, new Date());
     }
     
     /*
@@ -142,7 +143,8 @@ public class DeviceUniqueIndexTest extends TestCase {
         //-------------
         // Test null keys
         DeviceUniqueIndex idx2 = new DeviceUniqueIndex(
-                                             EnumSet.of(DeviceField.IPV4,
+                                             EnumSet.of(DeviceField.IPv4,
+                                            		 	DeviceField.IPv6,
                                                         DeviceField.SWITCH));
         // only one key field is null
         idx2.updateIndex(e3, 3L);
@@ -172,4 +174,4 @@ public class DeviceUniqueIndexTest extends TestCase {
         // anyways. We can now add d1 ;-)
         assertEquals(true, idx3.updateIndex(d1, 1L));
     }
-}
+}
\ No newline at end of file
diff --git a/src/test/java/net/floodlightcontroller/devicemanager/test/MockDevice.java b/src/test/java/net/floodlightcontroller/devicemanager/test/MockDevice.java
index f8c0bc3f082159a81fd6cdbfb0761ebf57182a00..0feff29e737ad5479c4ee9fd849828df7f4918a0 100644
--- a/src/test/java/net/floodlightcontroller/devicemanager/test/MockDevice.java
+++ b/src/test/java/net/floodlightcontroller/devicemanager/test/MockDevice.java
@@ -23,7 +23,10 @@ import java.util.Collection;
 import java.util.List;
 import java.util.TreeSet;
 
+import org.projectfloodlight.openflow.types.DatapathId;
 import org.projectfloodlight.openflow.types.IPv4Address;
+import org.projectfloodlight.openflow.types.IPv6Address;
+import org.projectfloodlight.openflow.types.OFPort;
 
 import net.floodlightcontroller.devicemanager.IEntityClass;
 import net.floodlightcontroller.devicemanager.SwitchPort;
@@ -62,20 +65,31 @@ public class MockDevice extends Device {
     public IPv4Address[] getIPv4Addresses() {
         TreeSet<IPv4Address> vals = new TreeSet<IPv4Address>();
         for (Entity e : entities) {
-            if (e.getIpv4Address() == null) continue;
+            if (e.getIpv4Address().equals(IPv4Address.NONE)) continue;
             vals.add(e.getIpv4Address());
         }
         
         return vals.toArray(new IPv4Address[vals.size()]);
     }
+    
+    @Override
+    public IPv6Address[] getIPv6Addresses() {
+        TreeSet<IPv6Address> vals = new TreeSet<IPv6Address>();
+        for (Entity e : entities) {
+            if (e.getIpv6Address().equals(IPv6Address.NONE)) continue;
+            vals.add(e.getIpv6Address());
+        }
+        
+        return vals.toArray(new IPv6Address[vals.size()]);
+    }
 
     @Override
     public SwitchPort[] getAttachmentPoints() {
         ArrayList<SwitchPort> vals = 
                 new ArrayList<SwitchPort>(entities.length);
         for (Entity e : entities) {
-            if (e.getSwitchDPID() != null &&
-                e.getSwitchPort() != null &&
+            if (!e.getSwitchDPID().equals(DatapathId.NONE) &&
+                !e.getSwitchPort().equals(OFPort.ZERO) &&
                 deviceManager.isValidAttachmentPoint(e.getSwitchDPID(), e.getSwitchPort())) {
                 SwitchPort sp = new SwitchPort(e.getSwitchDPID(), 
                                                e.getSwitchPort());
@@ -90,5 +104,4 @@ public class MockDevice extends Device {
         return "MockDevice [getEntityClass()=" + getEntityClass()
                + ", getEntities()=" + Arrays.toString(getEntities()) + "]";
     }
-    
-}
+}
\ No newline at end of file
diff --git a/src/test/java/net/floodlightcontroller/devicemanager/test/MockDeviceManager.java b/src/test/java/net/floodlightcontroller/devicemanager/test/MockDeviceManager.java
index 780e7eb8166383f1cbcf27e1aa3f5f5bdc5a87bd..33efb3253cc67dde0fb16bca1603ad3538d56e81 100644
--- a/src/test/java/net/floodlightcontroller/devicemanager/test/MockDeviceManager.java
+++ b/src/test/java/net/floodlightcontroller/devicemanager/test/MockDeviceManager.java
@@ -22,6 +22,7 @@ import java.util.List;
 
 import org.projectfloodlight.openflow.types.DatapathId;
 import org.projectfloodlight.openflow.types.IPv4Address;
+import org.projectfloodlight.openflow.types.IPv6Address;
 import org.projectfloodlight.openflow.types.MacAddress;
 import org.projectfloodlight.openflow.types.OFPort;
 import org.projectfloodlight.openflow.types.VlanVid;
@@ -62,56 +63,27 @@ public class MockDeviceManager extends DeviceManagerImpl {
 	/**
 	 * Learn a device using the given characteristics.
 	 * @param macAddress the MAC
-	 * @param vlan the VLAN (can be null)
-	 * @param ipv4Address the IP (can be null)
-	 * @param switchDPID the attachment point switch DPID (can be null)
-	 * @param switchPort the attachment point switch port (can be null)
+	 * @param vlan the VLAN (can be VlanVid.ZERO for untagged)
+	 * @param ipv4Address the IPv4 (can be IPv4Address.NONE)
+	 * @param ipv6Address the IPv6 (can be IPv6Address.NONE)
+	 * @param switchDPID the attachment point switch DPID (can be DatapathId.NONE)
+	 * @param switchPort the attachment point switch port (can be OFPort.ZERO)
 	 * @param processUpdates if false, will not send updates.  Note that this
 	 * method is not thread safe if this is false
 	 * @return the device, either new or not
 	 */
-	public IDevice learnEntity(long macAddress, Short vlan,
-			Integer ipv4Address, Long switchDPID,
-			Integer switchPort,
+	public IDevice learnEntity(MacAddress macAddress, VlanVid vlan,
+			IPv4Address ipv4Address, IPv6Address ipv6Address, DatapathId switchDPID,
+			OFPort switchPort,
 			boolean processUpdates) {
 		List<IDeviceListener> listeners = deviceListeners.getOrderedListeners();
 		if (!processUpdates) {
 			deviceListeners.clearListeners();
 		}
 		
-		VlanVid v;
-		IPv4Address i;
-		DatapathId d;
-		OFPort p;
-
-		if (vlan != null && vlan.shortValue() <= 0)
-			vlan = null;
-		if (ipv4Address != null && ipv4Address == 0)
-			ipv4Address = null;
-		
-		if (vlan == null) {
-			v = VlanVid.ofVlan(-1);
-		} else {
-			v = VlanVid.ofVlan(vlan);
-		}
-		if (ipv4Address == null) {
-			i = IPv4Address.NONE;
-		} else {
-			i = IPv4Address.of(ipv4Address);
-		}
-		if (switchDPID == null) {
-			d = DatapathId.of(0);
-		} else {
-			d = DatapathId.of(switchDPID.longValue());
-		}
-		if (switchPort == null) {
-			p = OFPort.ZERO;
-		} else {
-			p = OFPort.of(switchPort);
-		}
-		
-		IDevice res =  learnDeviceByEntity(new Entity(MacAddress.of(macAddress), 
-				v, i, d, p, new Date()));
+		/* Entity will enforce all but VLAN be non-null */
+		IDevice res =  learnDeviceByEntity(new Entity(macAddress, 
+				vlan, ipv4Address, ipv6Address, switchDPID, switchPort, new Date()));
 		// Restore listeners
 		if (listeners != null) {
 			for (IDeviceListener listener : listeners) {
@@ -129,17 +101,17 @@ public class MockDeviceManager extends DeviceManagerImpl {
 	/**
 	 * Learn a device using the given characteristics.
 	 * @param macAddress the MAC
-	 * @param vlan the VLAN (can be null)
-	 * @param ipv4Address the IP (can be null)
-	 * @param switchDPID the attachment point switch DPID (can be null)
-	 * @param switchPort the attachment point switch port (can be null)
+	 * @param vlan the VLAN (can be VlanVid.ZERO for untagged)
+	 * @param ipv4Address the IPv4 (can be IPv4Address.NONE)
+	 * @param ipv6Address the IPv6 (can be IPv6Address.NONE)
+	 * @param switchDPID the attachment point switch DPID (can be DatapathId.NONE)
+	 * @param switchPort the attachment point switch port (can be OFPort.ZERO)
 	 * @return the device, either new or not
 	 */
-	public IDevice learnEntity(long macAddress, Short vlan,
-			Integer ipv4Address, Long switchDPID,
-			Integer switchPort) {
-		return learnEntity(macAddress, vlan, ipv4Address,
-				switchDPID, switchPort, true);
+	public IDevice learnEntity(MacAddress macAddress, VlanVid vlan,
+			IPv4Address ipv4Address, IPv6Address ipv6Address, DatapathId switchDPID,
+			OFPort switchPort) {
+		return learnEntity(macAddress, vlan, ipv4Address, ipv6Address, switchDPID, switchPort, true);
 	}
 
 	@Override
@@ -171,4 +143,4 @@ public class MockDeviceManager extends DeviceManagerImpl {
 		super.init(fmc);
 		setSyncServiceIfNotSet(new MockSyncService());
 	}
-}
+}
\ No newline at end of file
diff --git a/src/test/java/net/floodlightcontroller/devicemanager/test/MockEntityClassifier.java b/src/test/java/net/floodlightcontroller/devicemanager/test/MockEntityClassifier.java
index 2d92357f453f21c2bf8cc5a8543168a65dd695e0..009369e7f55c5810308bc06324a1a0a7943a2237 100644
--- a/src/test/java/net/floodlightcontroller/devicemanager/test/MockEntityClassifier.java
+++ b/src/test/java/net/floodlightcontroller/devicemanager/test/MockEntityClassifier.java
@@ -59,5 +59,4 @@ public class MockEntityClassifier extends DefaultEntityClassifier {
     public EnumSet<IDeviceService.DeviceField> getKeyFields() {
         return EnumSet.of(MAC, VLAN, SWITCH, PORT);
     }
-
 }
\ No newline at end of file
diff --git a/src/test/java/net/floodlightcontroller/devicemanager/test/MockEntityClassifierMac.java b/src/test/java/net/floodlightcontroller/devicemanager/test/MockEntityClassifierMac.java
index a953d5c14dfc0b69c9a899810e37c453d087d636..edc25d9d890f8670d351a9aaaceed333a43750e7 100644
--- a/src/test/java/net/floodlightcontroller/devicemanager/test/MockEntityClassifierMac.java
+++ b/src/test/java/net/floodlightcontroller/devicemanager/test/MockEntityClassifierMac.java
@@ -23,6 +23,8 @@ import static net.floodlightcontroller.devicemanager.IDeviceService.DeviceField.
 
 import java.util.EnumSet;
 
+import org.projectfloodlight.openflow.types.DatapathId;
+
 import net.floodlightcontroller.devicemanager.IDeviceService;
 import net.floodlightcontroller.devicemanager.IEntityClass;
 import net.floodlightcontroller.devicemanager.IDeviceService.DeviceField;
@@ -59,11 +61,11 @@ public class MockEntityClassifierMac extends DefaultEntityClassifier {
         if (entity.getSwitchDPID() == null) {
             throw new IllegalArgumentException("Not all key fields specified."
                     + " Required fields: " + getKeyFields());
-        } else if (entity.getSwitchDPID().getLong() == 1L) {
+        } else if (entity.getSwitchDPID().equals(DatapathId.of(1))) {
             return testECMac1;
-        } else if (entity.getSwitchDPID().getLong() == 2L) {
+        } else if (entity.getSwitchDPID().equals(DatapathId.of(2))) {
             return testECMac2;
-        } else if (entity.getSwitchDPID().getLong() == -1L) {
+        } else if (entity.getSwitchDPID().equals(DatapathId.of(-1))) {
             return null;
         }
         return DefaultEntityClassifier.entityClass;
diff --git a/src/test/java/net/floodlightcontroller/devicemanager/test/MockFlexEntityClassifier.java b/src/test/java/net/floodlightcontroller/devicemanager/test/MockFlexEntityClassifier.java
index c02e34069e4130f6fb0a9c2466f7b59b4ca7a440..fbb17e36ba5b0a17228da3b9f85eda8a6e11c607 100644
--- a/src/test/java/net/floodlightcontroller/devicemanager/test/MockFlexEntityClassifier.java
+++ b/src/test/java/net/floodlightcontroller/devicemanager/test/MockFlexEntityClassifier.java
@@ -89,4 +89,4 @@ public class MockFlexEntityClassifier extends DefaultEntityClassifier {
     public EnumSet<IDeviceService.DeviceField> getKeyFields() {
         return EnumSet.of(MAC, VLAN, SWITCH, PORT);
     }
-}
+}
\ No newline at end of file
diff --git a/src/test/java/net/floodlightcontroller/firewall/FirewallTest.java b/src/test/java/net/floodlightcontroller/firewall/FirewallTest.java
index 1f5790736d88f532686feb46090ababa8ad37878..7cc6ef19fd40e641721125a9e5e093b318259453 100644
--- a/src/test/java/net/floodlightcontroller/firewall/FirewallTest.java
+++ b/src/test/java/net/floodlightcontroller/firewall/FirewallTest.java
@@ -57,6 +57,7 @@ import org.projectfloodlight.openflow.protocol.OFVersion;
 import org.projectfloodlight.openflow.protocol.match.MatchField;
 import org.projectfloodlight.openflow.types.DatapathId;
 import org.projectfloodlight.openflow.types.EthType;
+import org.projectfloodlight.openflow.types.IPv4Address;
 import org.projectfloodlight.openflow.types.IPv4AddressWithMask;
 import org.projectfloodlight.openflow.types.IpProtocol;
 import org.projectfloodlight.openflow.types.MacAddress;
@@ -151,10 +152,10 @@ public class FirewallTest extends FloodlightTestCase {
                 .setOpCode(ARP.OP_REQUEST)
                 .setHardwareAddressLength((byte)6)
                 .setProtocolAddressLength((byte)4)
-                .setSenderHardwareAddress(Ethernet.toMACAddress("00:44:33:22:11:00"))
-                .setSenderProtocolAddress(IPv4.toIPv4Address("192.168.1.1"))
-                .setTargetHardwareAddress(Ethernet.toMACAddress("00:00:00:00:00:00"))
-                .setTargetProtocolAddress(IPv4.toIPv4Address("192.168.1.2"))
+                .setSenderHardwareAddress(MacAddress.of("00:44:33:22:11:00"))
+                .setSenderProtocolAddress(IPv4Address.of("192.168.1.1"))
+                .setTargetHardwareAddress(MacAddress.of("00:00:00:00:00:00"))
+                .setTargetProtocolAddress(IPv4Address.of("192.168.1.2"))
                 .setPayload(new Data(new byte[] {0x01})));
 
         // Build a ARP packet
@@ -170,10 +171,10 @@ public class FirewallTest extends FloodlightTestCase {
                 .setOpCode(ARP.OP_REQUEST)
                 .setHardwareAddressLength((byte)6)
                 .setProtocolAddressLength((byte)4)
-                .setSenderHardwareAddress(Ethernet.toMACAddress("00:11:22:33:44:55"))
-                .setSenderProtocolAddress(IPv4.toIPv4Address("192.168.1.2"))
-                .setTargetHardwareAddress(Ethernet.toMACAddress("00:44:33:22:11:00"))
-                .setTargetProtocolAddress(IPv4.toIPv4Address("192.168.1.1"))
+                .setSenderHardwareAddress(MacAddress.of("00:11:22:33:44:55"))
+                .setSenderProtocolAddress(IPv4Address.of("192.168.1.2"))
+                .setTargetHardwareAddress(MacAddress.of("00:44:33:22:11:00"))
+                .setTargetProtocolAddress(IPv4Address.of("192.168.1.1"))
                 .setPayload(new Data(new byte[] {0x01})));
 
         // Build a broadcast IP packet
diff --git a/src/test/java/net/floodlightcontroller/forwarding/ForwardingTest.java b/src/test/java/net/floodlightcontroller/forwarding/ForwardingTest.java
index 45eac76cfb4577227f6cc6cd3bf1ae787aa0c407..51f1faefb541b21acefbe0151385aa1a7277ad9f 100644
--- a/src/test/java/net/floodlightcontroller/forwarding/ForwardingTest.java
+++ b/src/test/java/net/floodlightcontroller/forwarding/ForwardingTest.java
@@ -1,19 +1,19 @@
 /**
-*    Copyright 2011, Big Switch Networks, Inc.
-*    Originally created by David Erickson, Stanford University
-*
-*    Licensed under the Apache License, Version 2.0 (the "License"); you may
-*    not use this file except in compliance with the License. You may obtain
-*    a copy of the License at
-*
-*         http://www.apache.org/licenses/LICENSE-2.0
-*
-*    Unless required by applicable law or agreed to in writing, software
-*    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-*    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-*    License for the specific language governing permissions and limitations
-*    under the License.
-**/
+ *    Copyright 2011, Big Switch Networks, Inc.
+ *    Originally created by David Erickson, Stanford University
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License"); you may
+ *    not use this file except in compliance with the License. You may obtain
+ *    a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ *    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ *    License for the specific language governing permissions and limitations
+ *    under the License.
+ **/
 
 package net.floodlightcontroller.forwarding;
 
@@ -45,6 +45,7 @@ import net.floodlightcontroller.packet.Data;
 import net.floodlightcontroller.packet.Ethernet;
 import net.floodlightcontroller.packet.IPacket;
 import net.floodlightcontroller.packet.IPv4;
+import net.floodlightcontroller.packet.IPv6;
 import net.floodlightcontroller.packet.UDP;
 import net.floodlightcontroller.routing.IRoutingService;
 import net.floodlightcontroller.routing.Route;
@@ -73,12 +74,14 @@ import org.projectfloodlight.openflow.protocol.OFVersion;
 import org.projectfloodlight.openflow.types.DatapathId;
 import org.projectfloodlight.openflow.types.EthType;
 import org.projectfloodlight.openflow.types.IPv4Address;
+import org.projectfloodlight.openflow.types.IPv6Address;
 import org.projectfloodlight.openflow.types.IpProtocol;
 import org.projectfloodlight.openflow.types.MacAddress;
 import org.projectfloodlight.openflow.types.OFBufferId;
 import org.projectfloodlight.openflow.types.OFPort;
 import org.projectfloodlight.openflow.types.TransportPort;
 import org.projectfloodlight.openflow.types.U64;
+import org.projectfloodlight.openflow.types.VlanVid;
 import org.projectfloodlight.openflow.protocol.OFPacketInReason;
 import org.projectfloodlight.openflow.protocol.action.OFAction;
 import org.projectfloodlight.openflow.protocol.action.OFActionOutput;
@@ -86,435 +89,697 @@ import org.sdnplatform.sync.ISyncService;
 import org.sdnplatform.sync.test.MockSyncService;
 
 public class ForwardingTest extends FloodlightTestCase {
-    protected FloodlightContext cntx;
-    protected MockDeviceManager deviceManager;
-    protected IRoutingService routingEngine;
-    protected Forwarding forwarding;
-    protected ITopologyService topology;
-    protected MockThreadPoolService threadPool;
-    protected IOFSwitch sw1, sw2;
-    protected OFFeaturesReply swFeatures;
-    protected IDevice srcDevice, dstDevice1, dstDevice2;
-    protected OFPacketIn packetIn;
-    protected OFPacketOut packetOut;
-    protected OFPacketOut packetOutFlooded;
-    protected IPacket testPacket;
-    protected byte[] testPacketSerialized;
-    protected int expected_wildcards;
-    protected Date currentDate;
-    private MockSyncService mockSyncService;
-    private OFFactory factory = OFFactories.getFactory(OFVersion.OF_13);
-    
-
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-
-        cntx = new FloodlightContext();
-
-        // Module loader setup
-        /*
-        Collection<Class<? extends IFloodlightModule>> mods = new ArrayList<Class<? extends IFloodlightModule>>();
-        Collection<IFloodlightService> mockedServices = new ArrayList<IFloodlightService>();
-        mods.add(Forwarding.class);
-        routingEngine = createMock(IRoutingService.class);
-        topology = createMock(ITopologyService.class);
-        mockedServices.add(routingEngine);
-        mockedServices.add(topology);
-        FloodlightTestModuleLoader fml = new FloodlightTestModuleLoader();
-        fml.setupModules(mods, mockedServices);
-        mockFloodlightProvider =
-        		(MockFloodlightProvider) fml.getModuleByName(MockFloodlightProvider.class);
-        deviceManager =
-        		(MockDeviceManager) fml.getModuleByName(MockDeviceManager.class);
-        threadPool =
-        		(MockThreadPoolService) fml.getModuleByName(MockThreadPoolService.class);
-        forwarding =
-        		(Forwarding) fml.getModuleByName(Forwarding.class);
-        */
-        mockFloodlightProvider = getMockFloodlightProvider();
-        forwarding = new Forwarding();
-        threadPool = new MockThreadPoolService();
-        deviceManager = new MockDeviceManager();
-        routingEngine = createMock(IRoutingService.class);
-        topology = createMock(ITopologyService.class);
-        mockSyncService = new MockSyncService();
-        DefaultEntityClassifier entityClassifier = new DefaultEntityClassifier();
-
-
-        FloodlightModuleContext fmc = new FloodlightModuleContext();
-        fmc.addService(IFloodlightProviderService.class,
-                       mockFloodlightProvider);
-        fmc.addService(IThreadPoolService.class, threadPool);
-        fmc.addService(ITopologyService.class, topology);
-        fmc.addService(IRoutingService.class, routingEngine);
-        fmc.addService(IDeviceService.class, deviceManager);
-        fmc.addService(IEntityClassifierService.class, entityClassifier);
-        fmc.addService(ISyncService.class, mockSyncService);
-        fmc.addService(IDebugCounterService.class, new MockDebugCounterService());
-        fmc.addService(IDebugEventService.class, new MockDebugEventService());
-        fmc.addService(IOFSwitchService.class, getMockSwitchService());
-
-        topology.addListener(anyObject(ITopologyListener.class));
-        expectLastCall().anyTimes();
-        expect(topology.isIncomingBroadcastAllowed(anyObject(DatapathId.class), anyObject(OFPort.class))).andReturn(true).anyTimes();
-        replay(topology);
-
-        threadPool.init(fmc);
-        mockSyncService.init(fmc);
-        forwarding.init(fmc);
-        deviceManager.init(fmc);
-        entityClassifier.init(fmc);
-        threadPool.startUp(fmc);
-        mockSyncService.startUp(fmc);
-        deviceManager.startUp(fmc);
-        forwarding.startUp(fmc);
-        entityClassifier.startUp(fmc);
-        verify(topology);
-
-        swFeatures = factory.buildFeaturesReply().setNBuffers(1000).build();
-        // Mock switches
-        sw1 = EasyMock.createMock(IOFSwitch.class);
-        expect(sw1.getId()).andReturn(DatapathId.of(1L)).anyTimes();
-        expect(sw1.getOFFactory()).andReturn(factory).anyTimes();
-        expect(sw1.getBuffers()).andReturn(swFeatures.getNBuffers()).anyTimes();
-
-        sw2 = EasyMock.createMock(IOFSwitch.class);
-        expect(sw2.getId()).andReturn(DatapathId.of(2L)).anyTimes();
-        expect(sw2.getOFFactory()).andReturn(factory).anyTimes();
-        expect(sw2.getBuffers()).andReturn(swFeatures.getNBuffers()).anyTimes();
-
-        expect(sw1.hasAttribute(IOFSwitch.PROP_SUPPORTS_OFPP_TABLE)).andReturn(true).anyTimes();
-
-        expect(sw2.hasAttribute(IOFSwitch.PROP_SUPPORTS_OFPP_TABLE)).andReturn(true).anyTimes();
-
-        // Load the switch map
-        Map<DatapathId, IOFSwitch> switches = new HashMap<DatapathId, IOFSwitch>();
-        switches.put(DatapathId.of(1L), sw1);
-        switches.put(DatapathId.of(2L), sw2);
-        getMockSwitchService().setSwitches(switches);
-
-        // Build test packet
-        testPacket = new Ethernet()
-            .setDestinationMACAddress("00:11:22:33:44:55")
-            .setSourceMACAddress("00:44:33:22:11:00")
-            .setEtherType(EthType.IPv4)
-            .setPayload(
-                new IPv4()
-                .setTtl((byte) 128)
-                .setSourceAddress("192.168.1.1")
-                .setDestinationAddress("192.168.1.2")
-                .setPayload(new UDP()
-                            .setSourcePort((short) 5000)
-                            .setDestinationPort((short) 5001)
-                            .setPayload(new Data(new byte[] {0x01}))));
-
-
-
-        currentDate = new Date();
-
-        // Mock Packet-in
-        testPacketSerialized = testPacket.serialize();
-        packetIn = factory.buildPacketIn()
-        		.setMatch(factory.buildMatch()
-        				.setExact(MatchField.IN_PORT, OFPort.of(1))
-        				.setExact(MatchField.ETH_SRC, MacAddress.of("00:44:33:22:11:00"))
-        				.setExact(MatchField.ETH_DST, MacAddress.of("00:11:22:33:44:55"))
-        				.setExact(MatchField.ETH_TYPE, EthType.IPv4)
-        				.setExact(MatchField.IPV4_SRC, IPv4Address.of("192.168.1.1"))
-        				.setExact(MatchField.IPV4_DST, IPv4Address.of("192.168.1.2"))
-        				.setExact(MatchField.IP_PROTO, IpProtocol.UDP)
-        				.setExact(MatchField.UDP_SRC, TransportPort.of(5000))
-        				.setExact(MatchField.UDP_DST, TransportPort.of(5001))
-        				.build())
-        		.setBufferId(OFBufferId.NO_BUFFER)
-        		.setData(testPacketSerialized)
-        		.setReason(OFPacketInReason.NO_MATCH)
-                .build();
-
-        // Mock Packet-out
-        List<OFAction> poactions = new ArrayList<OFAction>();
-        poactions.add(factory.actions().output(OFPort.of(3), Integer.MAX_VALUE));
-        packetOut = factory.buildPacketOut()
-        		.setBufferId(this.packetIn.getBufferId())
-        		.setActions(poactions)
-        		.setInPort(OFPort.of(1))
-        		.setData(testPacketSerialized)
-        		.setXid(15)
-        		.build();
-
-        // Mock Packet-out with OFPP_FLOOD action
-        poactions = new ArrayList<OFAction>();
-        poactions.add(factory.actions().output(OFPort.FLOOD, Integer.MAX_VALUE));
-        packetOutFlooded = factory.buildPacketOut()
-        		.setBufferId(this.packetIn.getBufferId())
-        		.setInPort(packetIn.getMatch().get(MatchField.IN_PORT))
-        		.setXid(17)
-        		.setActions(poactions)
-        		.setData(testPacketSerialized)
-        		.build();
-            
-        IFloodlightProviderService.bcStore.
-            put(cntx,
-                IFloodlightProviderService.CONTEXT_PI_PAYLOAD,
-                (Ethernet)testPacket);
-    }
-
-    enum DestDeviceToLearn { NONE, DEVICE1 ,DEVICE2 };
-    public void learnDevices(DestDeviceToLearn destDeviceToLearn) {
-        // Build src and dest devices
-        MacAddress dataLayerSource = ((Ethernet)testPacket).getSourceMACAddress();
-        MacAddress dataLayerDest =
-                ((Ethernet)testPacket).getDestinationMACAddress();
-        IPv4Address networkSource =
-                ((IPv4)((Ethernet)testPacket).getPayload()).
-                    getSourceAddress();
-        IPv4Address networkDest =
-                ((IPv4)((Ethernet)testPacket).getPayload()).
-                    getDestinationAddress();
-
-        reset(topology);
-        expect(topology.isAttachmentPointPort(DatapathId.of(1L), OFPort.of(1)))
-                                              .andReturn(true)
-                                              .anyTimes();
-        expect(topology.isAttachmentPointPort(DatapathId.of(2L), OFPort.of(3)))
-                                              .andReturn(true)
-                                              .anyTimes();
-        expect(topology.isAttachmentPointPort(DatapathId.of(1L), OFPort.of(3)))
-                                              .andReturn(true)
-                                              .anyTimes();
-        replay(topology);
-
-        srcDevice =
-                deviceManager.learnEntity(dataLayerSource.getLong(),
-                                          null, networkSource.getInt(),
-                                          1L, 1);
-        IDeviceService.fcStore. put(cntx,
-                                    IDeviceService.CONTEXT_SRC_DEVICE,
-                                    srcDevice);
-        if (destDeviceToLearn == DestDeviceToLearn.DEVICE1) {
-            dstDevice1 =
-                    deviceManager.learnEntity(dataLayerDest.getLong(),
-                                              null, networkDest.getInt(),
-                                              2L, 3);
-            IDeviceService.fcStore.put(cntx,
-                                       IDeviceService.CONTEXT_DST_DEVICE,
-                                       dstDevice1);
-        }
-        if (destDeviceToLearn == DestDeviceToLearn.DEVICE2) {
-            dstDevice2 =
-                    deviceManager.learnEntity(dataLayerDest.getLong(),
-                                              null, networkDest.getInt(),
-                                              1L, 3);
-            IDeviceService.fcStore.put(cntx,
-                                       IDeviceService.CONTEXT_DST_DEVICE,
-                                       dstDevice2);
-        }
-        verify(topology);
-    }
-
-    @Test
-    public void testForwardMultiSwitchPath() throws Exception {
-        learnDevices(DestDeviceToLearn.DEVICE1);
-
-        Capture<OFMessage> wc1 = new Capture<OFMessage>(CaptureType.ALL);
-        Capture<OFMessage> wc2 = new Capture<OFMessage>(CaptureType.ALL);
-
-        Route route = new Route(DatapathId.of(1L), DatapathId.of(2L));
-        List<NodePortTuple> nptList = new ArrayList<NodePortTuple>();
-        nptList.add(new NodePortTuple(DatapathId.of(1L), OFPort.of(1)));
-        nptList.add(new NodePortTuple(DatapathId.of(1L), OFPort.of(3)));
-        nptList.add(new NodePortTuple(DatapathId.of(2L), OFPort.of(1)));
-        nptList.add(new NodePortTuple(DatapathId.of(2L), OFPort.of(3)));
-        route.setPath(nptList);
-        expect(routingEngine.getRoute(DatapathId.of(1L), OFPort.of(1), DatapathId.of(2L), OFPort.of(3), U64.ZERO)).andReturn(route).atLeastOnce();
-
-        // Expected Flow-mods
-        Match match = packetIn.getMatch();
-        OFActionOutput action = factory.actions().output(OFPort.of(3), Integer.MAX_VALUE);
-        List<OFAction> actions = new ArrayList<OFAction>();
-        actions.add(action);
-
-        OFFlowMod fm1 = factory.buildFlowAdd()
-        		.setIdleTimeout((short)5)
-        		.setMatch(match)
-        		.setActions(actions)
-        		.setOutPort(action.getPort())
-        		.setBufferId(OFBufferId.NO_BUFFER)
-        		.setCookie(U64.of(2L << 52))
-        		.setPriority(1)
-        		.build();
-        OFFlowMod fm2 = fm1.createBuilder().build();
-
-        sw1.write(capture(wc1));
-        expectLastCall().anyTimes();
-        sw2.write(capture(wc2));
-        expectLastCall().anyTimes();
-
-        reset(topology);
-        expect(topology.getL2DomainId(DatapathId.of(1L))).andReturn(DatapathId.of(1L)).anyTimes();
-        expect(topology.getL2DomainId(DatapathId.of(2L))).andReturn(DatapathId.of(1L)).anyTimes();
-        expect(topology.isAttachmentPointPort(DatapathId.of(1L),  OFPort.of(1))).andReturn(true).anyTimes();
-        expect(topology.isAttachmentPointPort(DatapathId.of(2L),  OFPort.of(3))).andReturn(true).anyTimes();
-        expect(topology.isIncomingBroadcastAllowed(DatapathId.of(anyLong()), OFPort.of(anyShort()))).andReturn(true).anyTimes();
-
-        // Reset mocks, trigger the packet in, and validate results
-        replay(sw1, sw2, routingEngine, topology);
-        forwarding.receive(sw1, this.packetIn, cntx);
-        verify(sw1, sw2, routingEngine);
-
-        assertTrue(wc1.hasCaptured());  // wc1 should get packetout + flowmod.
-        assertTrue(wc2.hasCaptured());  // wc2 should be a flowmod.
-
-        List<OFMessage> msglist = wc1.getValues();
-
-        for (OFMessage m: msglist) {
-            if (m instanceof OFFlowMod)
-                assertTrue(OFMessageUtils.equalsIgnoreXid(fm1, m));
-            else if (m instanceof OFPacketOut) {
-            	assertTrue(OFMessageUtils.equalsIgnoreXid(packetOut, m));
-            }
-        }
-
-        OFMessage m = wc2.getValue();
-        assert (m instanceof OFFlowMod);
-        assertTrue(OFMessageUtils.equalsIgnoreXid(m, fm2));
-    }
-
-    @Test
-    public void testForwardSingleSwitchPath() throws Exception {
-        learnDevices(DestDeviceToLearn.DEVICE2);
-
-        Capture<OFMessage> wc1 = new Capture<OFMessage>(CaptureType.ALL);
-        Capture<OFMessage> wc2 = new Capture<OFMessage>(CaptureType.ALL);
-        
-        Route route = new  Route(DatapathId.of(1L), DatapathId.of(1L));
-        route.getPath().add(new NodePortTuple(DatapathId.of(1L), OFPort.of(1)));
-        route.getPath().add(new NodePortTuple(DatapathId.of(1L), OFPort.of(3)));
-        expect(routingEngine.getRoute(DatapathId.of(1L), OFPort.of(1), DatapathId.of(1L), OFPort.of(3), U64.ZERO)).andReturn(route).atLeastOnce();
-
-        // Expected Flow-mods
-        Match match = packetIn.getMatch();
-        OFActionOutput action = factory.actions().output(OFPort.of(3), Integer.MAX_VALUE);
-        List<OFAction> actions = new ArrayList<OFAction>();
-        actions.add(action);
-
-        OFFlowMod fm1 = factory.buildFlowAdd()
-        	.setIdleTimeout((short)5)
-            .setMatch(match)
-            .setActions(actions)
-            .setOutPort(OFPort.of(3))
-            .setBufferId(OFBufferId.NO_BUFFER)
-            .setCookie(U64.of(2L<< 52))
-            .setPriority(1)
-            .build();
-                
-        // Record expected packet-outs/flow-mods
-        sw1.write(capture(wc1));
-        expectLastCall().once();
-        sw1.write(capture(wc2));
-        expectLastCall().once();
-        
-        reset(topology);
-        expect(topology.isIncomingBroadcastAllowed(DatapathId.of(anyLong()), OFPort.of(anyShort()))).andReturn(true).anyTimes();
-        expect(topology.getL2DomainId(DatapathId.of(1L))).andReturn(DatapathId.of(1L)).anyTimes();
-        expect(topology.isAttachmentPointPort(DatapathId.of(1L),  OFPort.of(1))).andReturn(true).anyTimes();
-        expect(topology.isAttachmentPointPort(DatapathId.of(1L),  OFPort.of(3))).andReturn(true).anyTimes();
-
-        // Reset mocks, trigger the packet in, and validate results
-        replay(sw1, sw2, routingEngine, topology);
-        forwarding.receive(sw1, this.packetIn, cntx);
-        verify(sw1, sw2, routingEngine);
-        
-        assertTrue(wc1.hasCaptured());
-        assertTrue(wc2.hasCaptured());
-        
-        assertTrue(OFMessageUtils.equalsIgnoreXid(wc1.getValue(), fm1));
-        assertTrue(OFMessageUtils.equalsIgnoreXid(wc2.getValue(), packetOut));
-    }
-
-    /*TODO OFMessageDamper broken due to XID variability in OFMessages... need to fix @Test */
-    public void testFlowModDampening() throws Exception {
-        learnDevices(DestDeviceToLearn.DEVICE2);
-
-        reset(topology);
-        expect(topology.isAttachmentPointPort(DatapathId.of(anyLong()), OFPort.of(anyShort())))
-        .andReturn(true).anyTimes();
-        expect(topology.getL2DomainId(DatapathId.of(1L))).andReturn(DatapathId.of(1L)).anyTimes();
-        replay(topology);
-
-
-        Route route = new  Route(DatapathId.of(1L), DatapathId.of(1L));
-        route.getPath().add(new NodePortTuple(DatapathId.of(1L), OFPort.of(1)));
-        route.getPath().add(new NodePortTuple(DatapathId.of(1L), OFPort.of(3)));
-        expect(routingEngine.getRoute(DatapathId.of(1L), OFPort.of(1), DatapathId.of(1L), OFPort.of(3), U64.ZERO)).andReturn(route).atLeastOnce();
-
-        // Expected Flow-mods
-        Match match = packetIn.getMatch();
-        OFActionOutput action = factory.actions().output(OFPort.of(3), Integer.MAX_VALUE);
-        List<OFAction> actions = new ArrayList<OFAction>();
-        actions.add(action);
-
-        OFFlowMod fm1 = factory.buildFlowAdd()
-        		.setIdleTimeout((short)5)
-        		.setMatch(match)
-        		.setActions(actions)
-        		.setOutPort(OFPort.of(3))
-        		.setBufferId(OFBufferId.NO_BUFFER)
-        		.setCookie(U64.of(2L << 52))
-        		.setXid(anyLong())
-        		.build();
-        
-        // Record expected packet-outs/flow-mods
-        // We will inject the packet_in 3 times and expect 1 flow mod and
-        // 3 packet outs due to flow mod dampening
-        sw1.write(fm1);
-        expectLastCall().times(1);
-        // Update new expected XID
-        sw1.write(packetOut.createBuilder().setXid(anyLong()).build());
-        expectLastCall().times(3);
-
-        reset(topology);
-        expect(topology.isIncomingBroadcastAllowed(DatapathId.of(anyLong()), OFPort.of(anyShort()))).andReturn(true).anyTimes();
-        expect(topology.getL2DomainId(DatapathId.of(1L))).andReturn(DatapathId.of(1L)).anyTimes();
-        expect(topology.isAttachmentPointPort(DatapathId.of(1L),  OFPort.of(1))).andReturn(true).anyTimes();
-        expect(topology.isAttachmentPointPort(DatapathId.of(1L),  OFPort.of(3))).andReturn(true).anyTimes();
-
-        // Reset mocks, trigger the packet in, and validate results
-        replay(sw1, routingEngine, topology);
-        forwarding.receive(sw1, this.packetIn, cntx);
-        forwarding.receive(sw1, this.packetIn, cntx);
-        forwarding.receive(sw1, this.packetIn, cntx);
-        verify(sw1, routingEngine);
-    }
-
-    @Test
-    public void testForwardNoPath() throws Exception {
-        learnDevices(DestDeviceToLearn.NONE);
-
-        // Set no destination attachment point or route
-        // expect no Flow-mod but expect the packet to be flooded
-        
-        Capture<OFMessage> wc1 = new Capture<OFMessage>(CaptureType.ALL);
-
-        // Reset mocks, trigger the packet in, and validate results
-        reset(topology);
-        expect(topology.isIncomingBroadcastAllowed(DatapathId.of(1L), OFPort.of(1))).andReturn(true).anyTimes();
-        expect(topology.isAttachmentPointPort(DatapathId.of(anyLong()),
-                                              OFPort.of(anyShort())))
-                                              .andReturn(true)
-                                              .anyTimes();
-        expect(sw1.hasAttribute(IOFSwitch.PROP_SUPPORTS_OFPP_FLOOD))
-                .andReturn(true).anyTimes();
-        // Reset XID to expected (dependent on prior unit tests)
-        sw1.write(capture(wc1));
-        expectLastCall().once();
-        replay(sw1, sw2, routingEngine, topology);
-        forwarding.receive(sw1, this.packetIn, cntx);
-        verify(sw1, sw2, routingEngine);
-        
-        assertTrue(wc1.hasCaptured());
-        assertTrue(OFMessageUtils.equalsIgnoreXid(wc1.getValue(), packetOutFlooded));
-    }
-
-}
+	protected FloodlightContext cntx;
+	protected MockDeviceManager deviceManager;
+	protected IRoutingService routingEngine;
+	protected Forwarding forwarding;
+	protected ITopologyService topology;
+	protected MockThreadPoolService threadPool;
+	protected IOFSwitch sw1, sw2;
+	protected OFFeaturesReply swFeatures;
+	protected IDevice srcDevice, dstDevice1, dstDevice2; /* reuse for IPv4 and IPv6 */
+	protected OFPacketIn packetIn;
+	protected OFPacketIn packetInIPv6;
+	protected OFPacketOut packetOut;
+	protected OFPacketOut packetOutIPv6;
+	protected OFPacketOut packetOutFlooded;
+	protected OFPacketOut packetOutFloodedIPv6;
+	protected IPacket testPacket;
+	protected IPacket testPacketIPv6;
+	protected byte[] testPacketSerialized;
+	protected byte[] testPacketSerializedIPv6;
+	protected int expected_wildcards;
+	protected Date currentDate;
+	private MockSyncService mockSyncService;
+	private OFFactory factory = OFFactories.getFactory(OFVersion.OF_13);
+
+
+	@Override
+	public void setUp() throws Exception {
+		super.setUp();
+
+		cntx = new FloodlightContext();
+
+		// Module loader setup
+		mockFloodlightProvider = getMockFloodlightProvider();
+		forwarding = new Forwarding();
+		threadPool = new MockThreadPoolService();
+		deviceManager = new MockDeviceManager();
+		routingEngine = createMock(IRoutingService.class);
+		topology = createMock(ITopologyService.class);
+		mockSyncService = new MockSyncService();
+		DefaultEntityClassifier entityClassifier = new DefaultEntityClassifier();
+
+		FloodlightModuleContext fmc = new FloodlightModuleContext();
+		fmc.addService(IFloodlightProviderService.class,
+				mockFloodlightProvider);
+		fmc.addService(IThreadPoolService.class, threadPool);
+		fmc.addService(ITopologyService.class, topology);
+		fmc.addService(IRoutingService.class, routingEngine);
+		fmc.addService(IDeviceService.class, deviceManager);
+		fmc.addService(IEntityClassifierService.class, entityClassifier);
+		fmc.addService(ISyncService.class, mockSyncService);
+		fmc.addService(IDebugCounterService.class, new MockDebugCounterService());
+		fmc.addService(IDebugEventService.class, new MockDebugEventService());
+		fmc.addService(IOFSwitchService.class, getMockSwitchService());
+
+		topology.addListener(anyObject(ITopologyListener.class));
+		expectLastCall().anyTimes();
+		expect(topology.isIncomingBroadcastAllowed(anyObject(DatapathId.class), anyObject(OFPort.class))).andReturn(true).anyTimes();
+		replay(topology);
+
+		threadPool.init(fmc);
+		mockSyncService.init(fmc);
+		forwarding.init(fmc);
+		deviceManager.init(fmc);
+		entityClassifier.init(fmc);
+		threadPool.startUp(fmc);
+		mockSyncService.startUp(fmc);
+		deviceManager.startUp(fmc);
+		forwarding.startUp(fmc);
+		entityClassifier.startUp(fmc);
+		verify(topology);
+
+		swFeatures = factory.buildFeaturesReply().setNBuffers(1000).build();
+		// Mock switches
+		sw1 = EasyMock.createMock(IOFSwitch.class);
+		expect(sw1.getId()).andReturn(DatapathId.of(1L)).anyTimes();
+		expect(sw1.getOFFactory()).andReturn(factory).anyTimes();
+		expect(sw1.getBuffers()).andReturn(swFeatures.getNBuffers()).anyTimes();
+
+		sw2 = EasyMock.createMock(IOFSwitch.class);
+		expect(sw2.getId()).andReturn(DatapathId.of(2L)).anyTimes();
+		expect(sw2.getOFFactory()).andReturn(factory).anyTimes();
+		expect(sw2.getBuffers()).andReturn(swFeatures.getNBuffers()).anyTimes();
+
+		expect(sw1.hasAttribute(IOFSwitch.PROP_SUPPORTS_OFPP_TABLE)).andReturn(true).anyTimes();
+
+		expect(sw2.hasAttribute(IOFSwitch.PROP_SUPPORTS_OFPP_TABLE)).andReturn(true).anyTimes();
+
+		// Load the switch map
+		Map<DatapathId, IOFSwitch> switches = new HashMap<DatapathId, IOFSwitch>();
+		switches.put(DatapathId.of(1L), sw1);
+		switches.put(DatapathId.of(2L), sw2);
+		getMockSwitchService().setSwitches(switches);
+
+		// Build test packet
+		testPacket = new Ethernet()
+		.setDestinationMACAddress("00:11:22:33:44:55")
+		.setSourceMACAddress("00:44:33:22:11:00")
+		.setEtherType(EthType.IPv4)
+		.setPayload(
+				new IPv4()
+				.setTtl((byte) 128)
+				.setSourceAddress("192.168.1.1")
+				.setDestinationAddress("192.168.1.2")
+				.setPayload(new UDP()
+				.setSourcePort((short) 5000)
+				.setDestinationPort((short) 5001)
+				.setPayload(new Data(new byte[] {0x01}))));
+
+		testPacketIPv6 = new Ethernet()
+		.setDestinationMACAddress("00:11:22:33:44:55")
+		.setSourceMACAddress("00:44:33:22:11:00")
+		.setEtherType(EthType.IPv6)
+		.setPayload(
+				new IPv6()
+				.setHopLimit((byte) 128)
+				.setSourceAddress(IPv6Address.of(1, 1))
+				.setDestinationAddress(IPv6Address.of(2, 2))
+				.setNextHeader(IpProtocol.UDP)
+				.setPayload(new UDP()
+				.setSourcePort((short) 5000)
+				.setDestinationPort((short) 5001)
+				.setPayload(new Data(new byte[] {0x01}))));
+
+		currentDate = new Date();
+
+		// Mock Packet-in
+		testPacketSerialized = testPacket.serialize();
+		testPacketSerializedIPv6 = testPacketIPv6.serialize();
+		
+		packetIn = factory.buildPacketIn()
+				.setMatch(factory.buildMatch()
+						.setExact(MatchField.IN_PORT, OFPort.of(1))
+						.setExact(MatchField.ETH_SRC, MacAddress.of("00:44:33:22:11:00"))
+						.setExact(MatchField.ETH_DST, MacAddress.of("00:11:22:33:44:55"))
+						.setExact(MatchField.ETH_TYPE, EthType.IPv4)
+						.setExact(MatchField.IPV4_SRC, IPv4Address.of("192.168.1.1"))
+						.setExact(MatchField.IPV4_DST, IPv4Address.of("192.168.1.2"))
+						.setExact(MatchField.IP_PROTO, IpProtocol.UDP)
+						.setExact(MatchField.UDP_SRC, TransportPort.of(5000))
+						.setExact(MatchField.UDP_DST, TransportPort.of(5001))
+						.build())
+						.setBufferId(OFBufferId.NO_BUFFER)
+						.setData(testPacketSerialized)
+						.setReason(OFPacketInReason.NO_MATCH)
+						.build();		
+		packetInIPv6 = factory.buildPacketIn()
+				.setMatch(factory.buildMatch()
+						.setExact(MatchField.IN_PORT, OFPort.of(1))
+						.setExact(MatchField.ETH_SRC, MacAddress.of("00:44:33:22:11:00"))
+						.setExact(MatchField.ETH_DST, MacAddress.of("00:11:22:33:44:55"))
+						.setExact(MatchField.ETH_TYPE, EthType.IPv6)
+						.setExact(MatchField.IPV6_SRC, IPv6Address.of(1, 1))
+						.setExact(MatchField.IPV6_DST, IPv6Address.of(2, 2))
+						.setExact(MatchField.IP_PROTO, IpProtocol.UDP)
+						.setExact(MatchField.UDP_SRC, TransportPort.of(5000))
+						.setExact(MatchField.UDP_DST, TransportPort.of(5001))
+						.build())
+						.setBufferId(OFBufferId.NO_BUFFER)
+						.setData(testPacketSerializedIPv6)
+						.setReason(OFPacketInReason.NO_MATCH)
+						.build();
+
+		// Mock Packet-out
+		List<OFAction> poactions = new ArrayList<OFAction>();
+		poactions.add(factory.actions().output(OFPort.of(3), Integer.MAX_VALUE));
+		packetOut = factory.buildPacketOut()
+				.setBufferId(this.packetIn.getBufferId())
+				.setActions(poactions)
+				.setInPort(OFPort.of(1))
+				.setData(testPacketSerialized)
+				.setXid(15)
+				.build();
+		packetOutIPv6 = factory.buildPacketOut()
+				.setBufferId(this.packetInIPv6.getBufferId())
+				.setActions(poactions)
+				.setInPort(OFPort.of(1))
+				.setData(testPacketSerializedIPv6)
+				.setXid(15)
+				.build();
+
+		// Mock Packet-out with OFPP_FLOOD action
+		poactions = new ArrayList<OFAction>();
+		poactions.add(factory.actions().output(OFPort.FLOOD, Integer.MAX_VALUE));
+		packetOutFlooded = factory.buildPacketOut()
+				.setBufferId(this.packetIn.getBufferId())
+				.setInPort(packetIn.getMatch().get(MatchField.IN_PORT))
+				.setXid(17)
+				.setActions(poactions)
+				.setData(testPacketSerialized)
+				.build();
+		packetOutFloodedIPv6 = factory.buildPacketOut()
+				.setBufferId(this.packetInIPv6.getBufferId())
+				.setInPort(packetInIPv6.getMatch().get(MatchField.IN_PORT))
+				.setXid(17)
+				.setActions(poactions)
+				.setData(testPacketSerializedIPv6)
+				.build();
+	}
+	
+	void removeDeviceFromContext() {
+		IFloodlightProviderService.bcStore.
+		remove(cntx,
+				IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
+		IFloodlightProviderService.bcStore.
+		remove(cntx,
+				IDeviceService.CONTEXT_SRC_DEVICE);
+		IFloodlightProviderService.bcStore.
+		remove(cntx,
+				IDeviceService.CONTEXT_DST_DEVICE);
+	}
+
+	enum DestDeviceToLearn { NONE, DEVICE1 ,DEVICE2 };
+	public void learnDevices(DestDeviceToLearn destDeviceToLearn) {
+		// Build src and dest devices
+		MacAddress dataLayerSource = ((Ethernet)testPacket).getSourceMACAddress();
+		MacAddress dataLayerDest =
+				((Ethernet)testPacket).getDestinationMACAddress();
+		IPv4Address networkSource =
+				((IPv4)((Ethernet)testPacket).getPayload()).
+				getSourceAddress();
+		IPv4Address networkDest =
+				((IPv4)((Ethernet)testPacket).getPayload()).
+				getDestinationAddress();
+
+		reset(topology);
+		expect(topology.isAttachmentPointPort(DatapathId.of(1L), OFPort.of(1)))
+		.andReturn(true)
+		.anyTimes();
+		expect(topology.isAttachmentPointPort(DatapathId.of(2L), OFPort.of(3)))
+		.andReturn(true)
+		.anyTimes();
+		expect(topology.isAttachmentPointPort(DatapathId.of(1L), OFPort.of(3)))
+		.andReturn(true)
+		.anyTimes();
+		replay(topology);
+
+		srcDevice =
+				deviceManager.learnEntity(dataLayerSource, VlanVid.ZERO, 
+						networkSource, IPv6Address.NONE,
+						DatapathId.of(1), OFPort.of(1));
+		IDeviceService.fcStore. put(cntx,
+				IDeviceService.CONTEXT_SRC_DEVICE,
+				srcDevice);
+		if (destDeviceToLearn == DestDeviceToLearn.DEVICE1) {
+			dstDevice1 =
+					deviceManager.learnEntity(dataLayerDest, VlanVid.ZERO, 
+							networkDest, IPv6Address.NONE,
+							DatapathId.of(2), OFPort.of(3));
+			IDeviceService.fcStore.put(cntx,
+					IDeviceService.CONTEXT_DST_DEVICE,
+					dstDevice1);
+		}
+		if (destDeviceToLearn == DestDeviceToLearn.DEVICE2) {
+			dstDevice2 =
+					deviceManager.learnEntity(dataLayerDest, VlanVid.ZERO, 
+							networkDest, IPv6Address.NONE,
+							DatapathId.of(1), OFPort.of(3));
+			IDeviceService.fcStore.put(cntx,
+					IDeviceService.CONTEXT_DST_DEVICE,
+					dstDevice2);
+		}
+		verify(topology);
+		
+		IFloodlightProviderService.bcStore.
+		put(cntx,
+				IFloodlightProviderService.CONTEXT_PI_PAYLOAD,
+				(Ethernet)testPacket);
+	}
+	
+	public void learnDevicesIPv6(DestDeviceToLearn destDeviceToLearn) {
+		// Build src and dest devices
+		MacAddress dataLayerSource = ((Ethernet)testPacketIPv6).getSourceMACAddress();
+		MacAddress dataLayerDest =
+				((Ethernet)testPacketIPv6).getDestinationMACAddress();
+		IPv6Address networkSource =
+				((IPv6)((Ethernet)testPacketIPv6).getPayload()).
+				getSourceAddress();
+		IPv6Address networkDest =
+				((IPv6)((Ethernet)testPacketIPv6).getPayload()).
+				getDestinationAddress();
+
+		reset(topology);
+		expect(topology.isAttachmentPointPort(DatapathId.of(1L), OFPort.of(1)))
+		.andReturn(true)
+		.anyTimes();
+		expect(topology.isAttachmentPointPort(DatapathId.of(2L), OFPort.of(3)))
+		.andReturn(true)
+		.anyTimes();
+		expect(topology.isAttachmentPointPort(DatapathId.of(1L), OFPort.of(3)))
+		.andReturn(true)
+		.anyTimes();
+		replay(topology);
+
+		srcDevice =
+				deviceManager.learnEntity(dataLayerSource, VlanVid.ZERO, 
+						IPv4Address.NONE, networkSource,
+						DatapathId.of(1), OFPort.of(1));
+		IDeviceService.fcStore.put(cntx,
+				IDeviceService.CONTEXT_SRC_DEVICE,
+				srcDevice);
+		if (destDeviceToLearn == DestDeviceToLearn.DEVICE1) {
+			dstDevice1 =
+					deviceManager.learnEntity(dataLayerDest, VlanVid.ZERO, 
+							IPv4Address.NONE, networkDest,
+							DatapathId.of(2), OFPort.of(3));
+			IDeviceService.fcStore.put(cntx,
+					IDeviceService.CONTEXT_DST_DEVICE,
+					dstDevice1);
+		}
+		if (destDeviceToLearn == DestDeviceToLearn.DEVICE2) {
+			dstDevice2 =
+					deviceManager.learnEntity(dataLayerDest, VlanVid.ZERO, 
+							 IPv4Address.NONE, networkDest,
+							DatapathId.of(1), OFPort.of(3));
+			IDeviceService.fcStore.put(cntx,
+					IDeviceService.CONTEXT_DST_DEVICE,
+					dstDevice2);
+		}
+		verify(topology);
+		
+		IFloodlightProviderService.bcStore.
+		put(cntx,
+				IFloodlightProviderService.CONTEXT_PI_PAYLOAD,
+				(Ethernet)testPacketIPv6);
+	}
+
+	@Test
+	public void testForwardMultiSwitchPath() throws Exception {
+		learnDevices(DestDeviceToLearn.DEVICE1);
+
+		Capture<OFMessage> wc1 = new Capture<OFMessage>(CaptureType.ALL);
+		Capture<OFMessage> wc2 = new Capture<OFMessage>(CaptureType.ALL);
+
+		Route route = new Route(DatapathId.of(1L), DatapathId.of(2L));
+		List<NodePortTuple> nptList = new ArrayList<NodePortTuple>();
+		nptList.add(new NodePortTuple(DatapathId.of(1L), OFPort.of(1)));
+		nptList.add(new NodePortTuple(DatapathId.of(1L), OFPort.of(3)));
+		nptList.add(new NodePortTuple(DatapathId.of(2L), OFPort.of(1)));
+		nptList.add(new NodePortTuple(DatapathId.of(2L), OFPort.of(3)));
+		route.setPath(nptList);
+		expect(routingEngine.getRoute(DatapathId.of(1L), OFPort.of(1), DatapathId.of(2L), OFPort.of(3), U64.ZERO)).andReturn(route).atLeastOnce();
+
+		// Expected Flow-mods
+		Match match = packetIn.getMatch();
+		OFActionOutput action = factory.actions().output(OFPort.of(3), Integer.MAX_VALUE);
+		List<OFAction> actions = new ArrayList<OFAction>();
+		actions.add(action);
+
+		OFFlowMod fm1 = factory.buildFlowAdd()
+				.setIdleTimeout((short)5)
+				.setMatch(match)
+				.setActions(actions)
+				.setOutPort(action.getPort())
+				.setBufferId(OFBufferId.NO_BUFFER)
+				.setCookie(U64.of(2L << 52))
+				.setPriority(1)
+				.build();
+		OFFlowMod fm2 = fm1.createBuilder().build();
+
+		sw1.write(capture(wc1));
+		expectLastCall().anyTimes();
+		sw2.write(capture(wc2));
+		expectLastCall().anyTimes();
+
+		reset(topology);
+		expect(topology.getL2DomainId(DatapathId.of(1L))).andReturn(DatapathId.of(1L)).anyTimes();
+		expect(topology.getL2DomainId(DatapathId.of(2L))).andReturn(DatapathId.of(1L)).anyTimes();
+		expect(topology.isAttachmentPointPort(DatapathId.of(1L),  OFPort.of(1))).andReturn(true).anyTimes();
+		expect(topology.isAttachmentPointPort(DatapathId.of(2L),  OFPort.of(3))).andReturn(true).anyTimes();
+		expect(topology.isIncomingBroadcastAllowed(DatapathId.of(anyLong()), OFPort.of(anyShort()))).andReturn(true).anyTimes();
+
+		// Reset mocks, trigger the packet in, and validate results
+		replay(sw1, sw2, routingEngine, topology);
+		forwarding.receive(sw1, this.packetIn, cntx);
+		verify(sw1, sw2, routingEngine);
+
+		assertTrue(wc1.hasCaptured());  // wc1 should get packetout + flowmod.
+		assertTrue(wc2.hasCaptured());  // wc2 should be a flowmod.
+
+		List<OFMessage> msglist = wc1.getValues();
+
+		for (OFMessage m: msglist) {
+			if (m instanceof OFFlowMod)
+				assertTrue(OFMessageUtils.equalsIgnoreXid(fm1, m));
+			else if (m instanceof OFPacketOut) {
+				assertTrue(OFMessageUtils.equalsIgnoreXid(packetOut, m));
+			}
+		}
+
+		OFMessage m = wc2.getValue();
+		assert (m instanceof OFFlowMod);
+		assertTrue(OFMessageUtils.equalsIgnoreXid(m, fm2));
+		
+		removeDeviceFromContext();
+	}
+	
+	@Test
+	public void testForwardMultiSwitchPathIPv6() throws Exception {
+		learnDevicesIPv6(DestDeviceToLearn.DEVICE1);
+
+		Capture<OFMessage> wc1 = new Capture<OFMessage>(CaptureType.ALL);
+		Capture<OFMessage> wc2 = new Capture<OFMessage>(CaptureType.ALL);
+
+		Route route = new Route(DatapathId.of(1L), DatapathId.of(2L));
+		List<NodePortTuple> nptList = new ArrayList<NodePortTuple>();
+		nptList.add(new NodePortTuple(DatapathId.of(1L), OFPort.of(1)));
+		nptList.add(new NodePortTuple(DatapathId.of(1L), OFPort.of(3)));
+		nptList.add(new NodePortTuple(DatapathId.of(2L), OFPort.of(1)));
+		nptList.add(new NodePortTuple(DatapathId.of(2L), OFPort.of(3)));
+		route.setPath(nptList);
+		expect(routingEngine.getRoute(DatapathId.of(1L), OFPort.of(1), DatapathId.of(2L), OFPort.of(3), U64.ZERO)).andReturn(route).atLeastOnce();
+
+		// Expected Flow-mods
+		Match match = packetInIPv6.getMatch();
+		OFActionOutput action = factory.actions().output(OFPort.of(3), Integer.MAX_VALUE);
+		List<OFAction> actions = new ArrayList<OFAction>();
+		actions.add(action);
+
+		OFFlowMod fm1 = factory.buildFlowAdd()
+				.setIdleTimeout((short)5)
+				.setMatch(match)
+				.setActions(actions)
+				.setOutPort(action.getPort())
+				.setBufferId(OFBufferId.NO_BUFFER)
+				.setCookie(U64.of(2L << 52))
+				.setPriority(1)
+				.build();
+		OFFlowMod fm2 = fm1.createBuilder().build();
+
+		sw1.write(capture(wc1));
+		expectLastCall().anyTimes();
+		sw2.write(capture(wc2));
+		expectLastCall().anyTimes();
+
+		reset(topology);
+		expect(topology.getL2DomainId(DatapathId.of(1L))).andReturn(DatapathId.of(1L)).anyTimes();
+		expect(topology.getL2DomainId(DatapathId.of(2L))).andReturn(DatapathId.of(1L)).anyTimes();
+		expect(topology.isAttachmentPointPort(DatapathId.of(1L),  OFPort.of(1))).andReturn(true).anyTimes();
+		expect(topology.isAttachmentPointPort(DatapathId.of(2L),  OFPort.of(3))).andReturn(true).anyTimes();
+		expect(topology.isIncomingBroadcastAllowed(DatapathId.of(anyLong()), OFPort.of(anyShort()))).andReturn(true).anyTimes();
+
+		// Reset mocks, trigger the packet in, and validate results
+		replay(sw1, sw2, routingEngine, topology);
+		forwarding.receive(sw1, this.packetInIPv6, cntx);
+		verify(sw1, sw2, routingEngine);
+
+		assertTrue(wc1.hasCaptured());  // wc1 should get packetout + flowmod.
+		assertTrue(wc2.hasCaptured());  // wc2 should be a flowmod.
+
+		List<OFMessage> msglist = wc1.getValues();
+
+		for (OFMessage m: msglist) {
+			if (m instanceof OFFlowMod)
+				assertTrue(OFMessageUtils.equalsIgnoreXid(fm1, m));
+			else if (m instanceof OFPacketOut) {
+				assertTrue(OFMessageUtils.equalsIgnoreXid(packetOutIPv6, m));
+			}
+		}
+
+		OFMessage m = wc2.getValue();
+		assert (m instanceof OFFlowMod);
+		assertTrue(OFMessageUtils.equalsIgnoreXid(m, fm2));
+		
+		removeDeviceFromContext();
+	}
+
+	@Test
+	public void testForwardSingleSwitchPath() throws Exception {
+		learnDevices(DestDeviceToLearn.DEVICE2);
+
+		Capture<OFMessage> wc1 = new Capture<OFMessage>(CaptureType.ALL);
+		Capture<OFMessage> wc2 = new Capture<OFMessage>(CaptureType.ALL);
+
+		Route route = new  Route(DatapathId.of(1L), DatapathId.of(1L));
+		route.getPath().add(new NodePortTuple(DatapathId.of(1L), OFPort.of(1)));
+		route.getPath().add(new NodePortTuple(DatapathId.of(1L), OFPort.of(3)));
+		expect(routingEngine.getRoute(DatapathId.of(1L), OFPort.of(1), DatapathId.of(1L), OFPort.of(3), U64.ZERO)).andReturn(route).atLeastOnce();
+
+		// Expected Flow-mods
+		Match match = packetIn.getMatch();
+		OFActionOutput action = factory.actions().output(OFPort.of(3), Integer.MAX_VALUE);
+		List<OFAction> actions = new ArrayList<OFAction>();
+		actions.add(action);
+
+		OFFlowMod fm1 = factory.buildFlowAdd()
+				.setIdleTimeout((short)5)
+				.setMatch(match)
+				.setActions(actions)
+				.setOutPort(OFPort.of(3))
+				.setBufferId(OFBufferId.NO_BUFFER)
+				.setCookie(U64.of(2L<< 52))
+				.setPriority(1)
+				.build();
+
+		// Record expected packet-outs/flow-mods
+		sw1.write(capture(wc1));
+		expectLastCall().once();
+		sw1.write(capture(wc2));
+		expectLastCall().once();
+
+		reset(topology);
+		expect(topology.isIncomingBroadcastAllowed(DatapathId.of(anyLong()), OFPort.of(anyShort()))).andReturn(true).anyTimes();
+		expect(topology.getL2DomainId(DatapathId.of(1L))).andReturn(DatapathId.of(1L)).anyTimes();
+		expect(topology.isAttachmentPointPort(DatapathId.of(1L),  OFPort.of(1))).andReturn(true).anyTimes();
+		expect(topology.isAttachmentPointPort(DatapathId.of(1L),  OFPort.of(3))).andReturn(true).anyTimes();
+
+		// Reset mocks, trigger the packet in, and validate results
+		replay(sw1, sw2, routingEngine, topology);
+		forwarding.receive(sw1, this.packetIn, cntx);
+		verify(sw1, sw2, routingEngine);
+
+		assertTrue(wc1.hasCaptured());
+		assertTrue(wc2.hasCaptured());
+
+		assertTrue(OFMessageUtils.equalsIgnoreXid(wc1.getValue(), fm1));
+		assertTrue(OFMessageUtils.equalsIgnoreXid(wc2.getValue(), packetOut));
+		
+		removeDeviceFromContext();
+	}
+	
+	@Test
+	public void testForwardSingleSwitchPathIPv6() throws Exception {
+		learnDevicesIPv6(DestDeviceToLearn.DEVICE2);
+
+		Capture<OFMessage> wc1 = new Capture<OFMessage>(CaptureType.ALL);
+		Capture<OFMessage> wc2 = new Capture<OFMessage>(CaptureType.ALL);
+
+		Route route = new  Route(DatapathId.of(1L), DatapathId.of(1L));
+		route.getPath().add(new NodePortTuple(DatapathId.of(1L), OFPort.of(1)));
+		route.getPath().add(new NodePortTuple(DatapathId.of(1L), OFPort.of(3)));
+		expect(routingEngine.getRoute(DatapathId.of(1L), OFPort.of(1), DatapathId.of(1L), OFPort.of(3), U64.ZERO)).andReturn(route).atLeastOnce();
+
+		// Expected Flow-mods
+		Match match = packetInIPv6.getMatch();
+		OFActionOutput action = factory.actions().output(OFPort.of(3), Integer.MAX_VALUE);
+		List<OFAction> actions = new ArrayList<OFAction>();
+		actions.add(action);
+
+		OFFlowMod fm1 = factory.buildFlowAdd()
+				.setIdleTimeout((short)5)
+				.setMatch(match)
+				.setActions(actions)
+				.setOutPort(OFPort.of(3))
+				.setBufferId(OFBufferId.NO_BUFFER)
+				.setCookie(U64.of(2L<< 52))
+				.setPriority(1)
+				.build();
+
+		// Record expected packet-outs/flow-mods
+		sw1.write(capture(wc1));
+		expectLastCall().once();
+		sw1.write(capture(wc2));
+		expectLastCall().once();
+
+		reset(topology);
+		expect(topology.isIncomingBroadcastAllowed(DatapathId.of(anyLong()), OFPort.of(anyShort()))).andReturn(true).anyTimes();
+		expect(topology.getL2DomainId(DatapathId.of(1L))).andReturn(DatapathId.of(1L)).anyTimes();
+		expect(topology.isAttachmentPointPort(DatapathId.of(1L),  OFPort.of(1))).andReturn(true).anyTimes();
+		expect(topology.isAttachmentPointPort(DatapathId.of(1L),  OFPort.of(3))).andReturn(true).anyTimes();
+
+		// Reset mocks, trigger the packet in, and validate results
+		replay(sw1, sw2, routingEngine, topology);
+		forwarding.receive(sw1, this.packetInIPv6, cntx);
+		verify(sw1, sw2, routingEngine);
+
+		assertTrue(wc1.hasCaptured());
+		assertTrue(wc2.hasCaptured());
+
+		assertTrue(OFMessageUtils.equalsIgnoreXid(wc1.getValue(), fm1));
+		assertTrue(OFMessageUtils.equalsIgnoreXid(wc2.getValue(), packetOutIPv6));
+		
+		removeDeviceFromContext();
+	}
+
+	/*TODO OFMessageDamper broken due to XID variability in OFMessages... need to fix @Test */
+	/*TODO make an IPv6 test for this once OFMessageDamper fixed */
+	public void testFlowModDampening() throws Exception {
+		learnDevices(DestDeviceToLearn.DEVICE2);
+
+		reset(topology);
+		expect(topology.isAttachmentPointPort(DatapathId.of(anyLong()), OFPort.of(anyShort())))
+		.andReturn(true).anyTimes();
+		expect(topology.getL2DomainId(DatapathId.of(1L))).andReturn(DatapathId.of(1L)).anyTimes();
+		replay(topology);
+
+
+		Route route = new  Route(DatapathId.of(1L), DatapathId.of(1L));
+		route.getPath().add(new NodePortTuple(DatapathId.of(1L), OFPort.of(1)));
+		route.getPath().add(new NodePortTuple(DatapathId.of(1L), OFPort.of(3)));
+		expect(routingEngine.getRoute(DatapathId.of(1L), OFPort.of(1), DatapathId.of(1L), OFPort.of(3), U64.ZERO)).andReturn(route).atLeastOnce();
+
+		// Expected Flow-mods
+		Match match = packetIn.getMatch();
+		OFActionOutput action = factory.actions().output(OFPort.of(3), Integer.MAX_VALUE);
+		List<OFAction> actions = new ArrayList<OFAction>();
+		actions.add(action);
+
+		OFFlowMod fm1 = factory.buildFlowAdd()
+				.setIdleTimeout((short)5)
+				.setMatch(match)
+				.setActions(actions)
+				.setOutPort(OFPort.of(3))
+				.setBufferId(OFBufferId.NO_BUFFER)
+				.setCookie(U64.of(2L << 52))
+				.setXid(anyLong())
+				.build();
+
+		// Record expected packet-outs/flow-mods
+		// We will inject the packet_in 3 times and expect 1 flow mod and
+		// 3 packet outs due to flow mod dampening
+		sw1.write(fm1);
+		expectLastCall().times(1);
+		// Update new expected XID
+		sw1.write(packetOut.createBuilder().setXid(anyLong()).build());
+		expectLastCall().times(3);
+
+		reset(topology);
+		expect(topology.isIncomingBroadcastAllowed(DatapathId.of(anyLong()), OFPort.of(anyShort()))).andReturn(true).anyTimes();
+		expect(topology.getL2DomainId(DatapathId.of(1L))).andReturn(DatapathId.of(1L)).anyTimes();
+		expect(topology.isAttachmentPointPort(DatapathId.of(1L),  OFPort.of(1))).andReturn(true).anyTimes();
+		expect(topology.isAttachmentPointPort(DatapathId.of(1L),  OFPort.of(3))).andReturn(true).anyTimes();
+
+		// Reset mocks, trigger the packet in, and validate results
+		replay(sw1, routingEngine, topology);
+		forwarding.receive(sw1, this.packetIn, cntx);
+		forwarding.receive(sw1, this.packetIn, cntx);
+		forwarding.receive(sw1, this.packetIn, cntx);
+		verify(sw1, routingEngine);
+		
+		removeDeviceFromContext();
+	}
+
+	@Test
+	public void testForwardNoPath() throws Exception {
+		learnDevices(DestDeviceToLearn.NONE);
+
+		// Set no destination attachment point or route
+		// expect no Flow-mod but expect the packet to be flooded
+
+		Capture<OFMessage> wc1 = new Capture<OFMessage>(CaptureType.ALL);
+
+		// Reset mocks, trigger the packet in, and validate results
+		reset(topology);
+		expect(topology.isIncomingBroadcastAllowed(DatapathId.of(1L), OFPort.of(1))).andReturn(true).anyTimes();
+		expect(topology.isAttachmentPointPort(DatapathId.of(anyLong()),
+				OFPort.of(anyShort())))
+				.andReturn(true)
+				.anyTimes();
+		expect(sw1.hasAttribute(IOFSwitch.PROP_SUPPORTS_OFPP_FLOOD))
+		.andReturn(true).anyTimes();
+		// Reset XID to expected (dependent on prior unit tests)
+		sw1.write(capture(wc1));
+		expectLastCall().once();
+		replay(sw1, sw2, routingEngine, topology);
+		forwarding.receive(sw1, this.packetIn, cntx);
+		verify(sw1, sw2, routingEngine);
+
+		assertTrue(wc1.hasCaptured());
+		assertTrue(OFMessageUtils.equalsIgnoreXid(wc1.getValue(), packetOutFlooded));
+		
+		removeDeviceFromContext();
+	}
+
+	@Test
+	public void testForwardNoPathIPv6() throws Exception {
+		learnDevicesIPv6(DestDeviceToLearn.NONE);
+
+		// Set no destination attachment point or route
+		// expect no Flow-mod but expect the packet to be flooded
+
+		Capture<OFMessage> wc1 = new Capture<OFMessage>(CaptureType.ALL);
+
+		// Reset mocks, trigger the packet in, and validate results
+		reset(topology);
+		expect(topology.isIncomingBroadcastAllowed(DatapathId.of(1L), OFPort.of(1))).andReturn(true).anyTimes();
+		expect(topology.isAttachmentPointPort(DatapathId.of(anyLong()),
+				OFPort.of(anyShort())))
+				.andReturn(true)
+				.anyTimes();
+		expect(sw1.hasAttribute(IOFSwitch.PROP_SUPPORTS_OFPP_FLOOD))
+		.andReturn(true).anyTimes();
+		// Reset XID to expected (dependent on prior unit tests)
+		sw1.write(capture(wc1));
+		expectLastCall().once();
+		replay(sw1, sw2, routingEngine, topology);
+		forwarding.receive(sw1, this.packetInIPv6, cntx);
+		verify(sw1, sw2, routingEngine);
+
+		assertTrue(wc1.hasCaptured());
+		assertTrue(OFMessageUtils.equalsIgnoreXid(wc1.getValue(), packetOutFloodedIPv6));
+		
+		removeDeviceFromContext();
+	}
+}
\ No newline at end of file
diff --git a/src/test/java/net/floodlightcontroller/loadbalancer/LoadBalancerTest.java b/src/test/java/net/floodlightcontroller/loadbalancer/LoadBalancerTest.java
index 231db1828741acd313defd362be9559f575f8df0..0aa8d933ea11f233eaad23cd3d94847503d64ec3 100644
--- a/src/test/java/net/floodlightcontroller/loadbalancer/LoadBalancerTest.java
+++ b/src/test/java/net/floodlightcontroller/loadbalancer/LoadBalancerTest.java
@@ -47,11 +47,13 @@ import org.projectfloodlight.openflow.protocol.OFPacketOut;
 import org.projectfloodlight.openflow.protocol.OFVersion;
 import org.projectfloodlight.openflow.types.EthType;
 import org.projectfloodlight.openflow.types.IPv4Address;
+import org.projectfloodlight.openflow.types.IPv6Address;
 import org.projectfloodlight.openflow.types.IpProtocol;
 import org.projectfloodlight.openflow.types.MacAddress;
 import org.projectfloodlight.openflow.types.OFBufferId;
 import org.projectfloodlight.openflow.types.OFPort;
 import org.projectfloodlight.openflow.types.U64;
+import org.projectfloodlight.openflow.types.VlanVid;
 import org.projectfloodlight.openflow.protocol.OFPacketInReason;
 import org.projectfloodlight.openflow.protocol.action.OFAction;
 import org.projectfloodlight.openflow.protocol.match.MatchField;
@@ -490,10 +492,10 @@ public class LoadBalancerTest extends FloodlightTestCase {
 				.setHardwareAddressLength((byte) 6)
 				.setProtocolAddressLength((byte) 4)
 				.setOpCode(ARP.OP_REQUEST)
-				.setSenderHardwareAddress(HexString.fromHexString("00:00:00:00:00:01"))
-				.setSenderProtocolAddress(IPv4.toIPv4AddressBytes("10.0.0.1"))
-				.setTargetHardwareAddress(HexString.fromHexString("00:00:00:00:00:00"))
-				.setTargetProtocolAddress(IPv4.toIPv4AddressBytes("10.0.0.100")));
+				.setSenderHardwareAddress(MacAddress.of("00:00:00:00:00:01"))
+				.setSenderProtocolAddress(IPv4Address.of("10.0.0.1"))
+				.setTargetHardwareAddress(MacAddress.of("00:00:00:00:00:00"))
+				.setTargetProtocolAddress(IPv4Address.of("10.0.0.100")));
 
 		arpRequest1Serialized = arpRequest1.serialize();
 
@@ -522,10 +524,10 @@ public class LoadBalancerTest extends FloodlightTestCase {
 				.setHardwareAddressLength((byte) 6)
 				.setProtocolAddressLength((byte) 4)
 				.setOpCode(ARP.OP_REPLY)
-				.setSenderHardwareAddress(HexString.fromHexString(LBVip.LB_PROXY_MAC))
-				.setSenderProtocolAddress(IPv4.toIPv4AddressBytes("10.0.0.100"))
-				.setTargetHardwareAddress(HexString.fromHexString("00:00:00:00:00:01"))
-				.setTargetProtocolAddress(IPv4.toIPv4AddressBytes("10.0.0.1")));
+				.setSenderHardwareAddress(MacAddress.of(LBVip.LB_PROXY_MAC))
+				.setSenderProtocolAddress(IPv4Address.of("10.0.0.100"))
+				.setTargetHardwareAddress(MacAddress.of("00:00:00:00:00:01"))
+				.setTargetProtocolAddress(IPv4Address.of("10.0.0.1")));
 
 		arpReply1Serialized = arpReply1.serialize();
 
@@ -621,18 +623,18 @@ public class LoadBalancerTest extends FloodlightTestCase {
 		MacAddress dataLayerDest2 = MacAddress.of("00:00:00:00:00:04");
 		IPv4Address networkDest2 = IPv4Address.of("10.0.0.4");
 
-		deviceManager.learnEntity(dataLayerSource1.getLong(),
-				null, networkSource1.getInt(),
-				1L, 1);
-		deviceManager.learnEntity(dataLayerSource2.getLong(),
-				null, networkSource2.getInt(),
-				1L, 2);
-		deviceManager.learnEntity(dataLayerDest1.getLong(),
-				null, networkDest1.getInt(),
-				1L, 3);
-		deviceManager.learnEntity(dataLayerDest2.getLong(),
-				null, networkDest2.getInt(),
-				1L, 4);
+		deviceManager.learnEntity(dataLayerSource1,
+				VlanVid.ZERO, networkSource1, IPv6Address.NONE,
+				DatapathId.of(1), OFPort.of(1));
+		deviceManager.learnEntity(dataLayerSource2,
+				VlanVid.ZERO, networkSource2, IPv6Address.NONE,
+				DatapathId.of(1), OFPort.of(2));
+		deviceManager.learnEntity(dataLayerDest1,
+				VlanVid.ZERO, networkDest1, IPv6Address.NONE,
+				DatapathId.of(1), OFPort.of(3));
+		deviceManager.learnEntity(dataLayerDest2,
+				VlanVid.ZERO, networkDest2, IPv6Address.NONE,
+				DatapathId.of(1), OFPort.of(4));
 
 		// in bound #1
 		Route route1 = new Route(DatapathId.of(1L), DatapathId.of(1L));
diff --git a/src/test/java/net/floodlightcontroller/packet/IPv6Test.java b/src/test/java/net/floodlightcontroller/packet/IPv6Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..78607cd836de138a23de516745f2b63aea88028b
--- /dev/null
+++ b/src/test/java/net/floodlightcontroller/packet/IPv6Test.java
@@ -0,0 +1,58 @@
+package net.floodlightcontroller.packet;
+
+import static org.junit.Assert.assertTrue;
+import java.util.Arrays;
+import org.junit.Test;
+import org.projectfloodlight.openflow.types.IPv6Address;
+import org.projectfloodlight.openflow.types.IpProtocol;
+
+/**
+ * @author Jacob Chappell (jacob.chappell@uky.edu)
+ */
+public class IPv6Test {
+    @Test
+    public void testSerializeWithoutPayload() {
+        byte[] expected = new byte[] {
+                0x64, 0x2B, 0x16, (byte) 0x95, 0x00, 0x00,
+                0x11, (byte) 0xE1, (byte) 0xFE, (byte) 0x80,
+                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                0x7A, (byte) 0xC5, (byte) 0xFF, (byte) 0xFE,
+                0x2E, 0x77, 0x35, (byte) 0xFE, (byte) 0x80,
+                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                0x77, 0x5D, (byte) 0xFF, (byte) 0xFE,
+                (byte) 0xC2, 0x30, (byte) 0xFD
+        };
+        IPv6 packet = (new IPv6())
+            .setTrafficClass((byte) 0x42)
+            .setFlowLabel(0xB1695)
+            .setPayloadLength((short) 0)
+            .setNextHeader(IpProtocol.of((short) 0x11))
+            .setHopLimit((byte) 0xE1)
+            .setSourceAddress(IPv6Address.of("fe80::7a:c5ff:fe2e:7735"))
+            .setDestinationAddress(IPv6Address.of("fe80::77:5dff:fec2:30fd"));
+        byte[] actual = packet.serialize();
+        assertTrue(Arrays.equals(expected, actual));
+    }
+
+    @Test
+    public void testDeserialize() throws PacketParsingException {
+        byte[] spudPacket = {
+                0x64, 0x2B, 0x16, (byte) 0x95, 0x00, 0x15,
+                0x11, (byte) 0xE1, (byte) 0xFE, (byte) 0x80,
+                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                0x7A, (byte) 0xC5, (byte) 0xFF, (byte) 0xFE,
+                0x2E, 0x77, 0x35, (byte) 0xFE, (byte) 0x80,
+                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                0x77, 0x5D, (byte) 0xFF, (byte) 0xFE,
+                (byte) 0xC2, 0x30, (byte) 0xFD, (byte) 0xD2,
+                0x01, 0x05, 0x7A, 0x00, 0x15, (byte) 0xF6,
+                (byte) 0xC8, (byte) 0xD8, 0x00, 0x00,
+                (byte) 0xD8, 0x4A, (byte) 0xC3, (byte) 0xF2,
+                0x02, 0x44, 0x75, (byte) 0x97, 0x69, 0x40
+        };
+        IPv6 packet = new IPv6();
+        packet.deserialize(spudPacket, 0, spudPacket.length);
+        byte[] packetSerialized = packet.serialize();
+        assertTrue(Arrays.equals(spudPacket, packetSerialized));
+    }
+}
diff --git a/src/test/java/net/floodlightcontroller/packet/PacketTest.java b/src/test/java/net/floodlightcontroller/packet/PacketTest.java
index f62f1a8cbc1a1a3d02426dab629b842d490e0368..65fafb9dd8ed8afadd33171743edc8b825db5d29 100644
--- a/src/test/java/net/floodlightcontroller/packet/PacketTest.java
+++ b/src/test/java/net/floodlightcontroller/packet/PacketTest.java
@@ -18,11 +18,11 @@ package net.floodlightcontroller.packet;
 
 import static org.junit.Assert.*;
 
-import java.util.Arrays;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.projectfloodlight.openflow.types.EthType;
+import org.projectfloodlight.openflow.types.IPv4Address;
+import org.projectfloodlight.openflow.types.MacAddress;
 
 public class PacketTest {
     protected IPacket pkt1, pkt2, pkt3, pkt4;
@@ -57,10 +57,10 @@ public class PacketTest {
                     .setHardwareAddressLength((byte) 6)
                     .setProtocolAddressLength((byte) 4)
                     .setOpCode(ARP.OP_REPLY)
-                    .setSenderHardwareAddress(Ethernet.toMACAddress("00:44:33:22:11:01"))
-                    .setSenderProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.1"))
-                    .setTargetHardwareAddress(Ethernet.toMACAddress("00:11:22:33:44:55"))
-                    .setTargetProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.2")));
+                    .setSenderHardwareAddress(MacAddress.of("00:44:33:22:11:01"))
+                    .setSenderProtocolAddress(IPv4Address.of("192.168.1.1"))
+                    .setTargetHardwareAddress(MacAddress.of("00:11:22:33:44:55"))
+                    .setTargetProtocolAddress(IPv4Address.of("192.168.1.2")));
         
         
         this.pkt3 = new Ethernet()
@@ -74,10 +74,10 @@ public class PacketTest {
                     .setHardwareAddressLength((byte) 6)
                     .setProtocolAddressLength((byte) 4)
                     .setOpCode(ARP.OP_REPLY)
-                    .setSenderHardwareAddress(Ethernet.toMACAddress("00:44:33:22:11:01"))
-                    .setSenderProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.1"))
-                    .setTargetHardwareAddress(Ethernet.toMACAddress("00:11:22:33:44:55"))
-                    .setTargetProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.2")));
+                    .setSenderHardwareAddress(MacAddress.of("00:44:33:22:11:01"))
+                    .setSenderProtocolAddress(IPv4Address.of("192.168.1.1"))
+                    .setTargetHardwareAddress(MacAddress.of("00:11:22:33:44:55"))
+                    .setTargetProtocolAddress(IPv4Address.of("192.168.1.2")));
         
         this.pkt4 = new Ethernet()
         .setDestinationMACAddress("FF:FF:FF:FF:FF:FF")
@@ -124,9 +124,8 @@ public class PacketTest {
         if (pkt instanceof ARP) {
             ARP arp = (ARP)pkt;
             ARP newArp = (ARP)newPkt;
-            newArp.setSenderProtocolAddress(new byte[] {1,2,3,4});
-            assertEquals(false, Arrays.equals(newArp.getSenderProtocolAddress(),
-                                              arp.getSenderProtocolAddress()));
+            newArp.setSenderProtocolAddress(IPv4Address.of(new byte[] {1,2,3,4}));
+            assertEquals(false, newArp.getSenderProtocolAddress().equals(arp.getSenderProtocolAddress()));
             assertEquals(false, newPkt.equals(pkt));
         } else {
             byte[] dummyData = dummyPkt.serialize().clone();
diff --git a/src/test/java/net/floodlightcontroller/packet/SPUDTest.java b/src/test/java/net/floodlightcontroller/packet/SPUDTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..82cd29e2d6e30e8d632d97869de388882e403042
--- /dev/null
+++ b/src/test/java/net/floodlightcontroller/packet/SPUDTest.java
@@ -0,0 +1,114 @@
+package net.floodlightcontroller.packet;
+
+import static org.junit.Assert.assertTrue;
+import java.util.Arrays;
+import org.junit.Test;
+
+/**
+ * @author Jacob Chappell (jacob.chappell@uky.edu)
+ */
+public class SPUDTest {
+    @Test
+    public void testSerializeCommandOpen() {
+        byte[] expected = new byte[] {
+                (byte) 0xd8, 0x00, 0x00, (byte) 0xd8,
+                (byte) 0xb6, 0x40, 0x17, (byte) 0x88,
+                0x0a, 0x51, 0x01, 0x07, 0x40
+        };
+        SPUD packet = (new SPUD())
+                .setTubeID(0xb64017880a510107L)
+                .setCommand(SPUD.COMMAND_OPEN)
+                .setADEC(false)
+                .setPDEC(false)
+                .setReserved((byte) 0);
+        byte[] actual = packet.serialize();
+        assertTrue(Arrays.equals(expected, actual));
+    }
+
+    @Test
+    public void testSerializeCommandDataEmpty() {
+        byte[] expected = new byte[] {
+                (byte) 0xd8, 0x00, 0x00, (byte) 0xd8,
+                (byte) 0xb6, 0x40, 0x17, (byte) 0x88,
+                0x0a, 0x51, 0x01, 0x07, 0x00
+        };
+        SPUD packet = (new SPUD())
+                .setTubeID(0xb64017880a510107L)
+                .setCommand(SPUD.COMMAND_DATA)
+                .setADEC(false)
+                .setPDEC(false)
+                .setReserved((byte) 0);
+        byte[] actual = packet.serialize();
+        assertTrue(Arrays.equals(expected, actual));
+    }
+
+    @Test
+    public void testSerializeCommandDataEmptyWithADEC() {
+        byte[] expected = new byte[] {
+                (byte) 0xd8, 0x00, 0x00, (byte) 0xd8,
+                (byte) 0xb6, 0x40, 0x17, (byte) 0x88,
+                0x0a, 0x51, 0x01, 0x07, 0x20
+        };
+        SPUD packet = (new SPUD())
+                .setTubeID(0xb64017880a510107L)
+                .setCommand(SPUD.COMMAND_DATA)
+                .setADEC(true)
+                .setPDEC(false)
+                .setReserved((byte) 0);
+        byte[] actual = packet.serialize();
+        assertTrue(Arrays.equals(expected, actual));
+    }
+
+    @Test
+    public void testSerializeCommandDataEmptyWithPDEC() {
+        byte[] expected = new byte[] {
+                (byte) 0xd8, 0x00, 0x00, (byte) 0xd8,
+                (byte) 0xb6, 0x40, 0x17, (byte) 0x88,
+                0x0a, 0x51, 0x01, 0x07, 0x10
+        };
+        SPUD packet = (new SPUD())
+                .setTubeID(0xb64017880a510107L)
+                .setCommand(SPUD.COMMAND_DATA)
+                .setADEC(false)
+                .setPDEC(true)
+                .setReserved((byte) 0);
+        byte[] actual = packet.serialize();
+        assertTrue(Arrays.equals(expected, actual));
+    }
+
+    @Test
+    public void testSerializeCommandDataEmptyWithBoth() {
+        byte[] expected = new byte[] {
+                (byte) 0xd8, 0x00, 0x00, (byte) 0xd8,
+                (byte) 0xb6, 0x40, 0x17, (byte) 0x88,
+                0x0a, 0x51, 0x01, 0x07, 0x30
+        };
+        SPUD packet = (new SPUD())
+                .setTubeID(0xb64017880a510107L)
+                .setCommand(SPUD.COMMAND_DATA)
+                .setADEC(true)
+                .setPDEC(true)
+                .setReserved((byte) 0);
+        byte[] actual = packet.serialize();
+        assertTrue(Arrays.equals(expected, actual));
+    }
+
+    @Test
+    public void testDeserialize() throws PacketParsingException {
+        byte[] spudPacket =  {
+                (byte) 0xd8, 0x00, 0x00, (byte) 0xd8, (byte) 0xb6,
+                0x40, 0x17, (byte) 0x88, 0x0a, 0x51, 0x01, 0x07,
+                0x00, (byte) 0xa1, 0x00, (byte) 0xa2, 0x68, 0x75,
+                0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x65,
+                0x4a, 0x61, 0x63, 0x6f, 0x62, 0x67, 0x6d, 0x65,
+                0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x68, 0x61,
+                0x73, 0x20, 0x6a, 0x6f, 0x69, 0x6e, 0x65, 0x64,
+                0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x6f, 0x6f,
+                0x6d
+        };
+        SPUD packet = new SPUD();
+        packet.deserialize(spudPacket, 0, spudPacket.length);
+        byte[] packetSerialized = packet.serialize();
+        assertTrue(Arrays.equals(spudPacket, packetSerialized));
+    }
+}
diff --git a/src/test/java/net/floodlightcontroller/packet/UDPTest.java b/src/test/java/net/floodlightcontroller/packet/UDPTest.java
index bed42a2e86dea1344e2963b2d56c1cf6585f63f1..3224c2747f0f33a13132d2cdd5b52c18f35eaa69 100644
--- a/src/test/java/net/floodlightcontroller/packet/UDPTest.java
+++ b/src/test/java/net/floodlightcontroller/packet/UDPTest.java
@@ -1,7 +1,7 @@
 /**
-*    Copyright 2011, Big Switch Networks, Inc. 
+*    Copyright 2011, Big Switch Networks, Inc.
 *    Originally created by David Erickson, Stanford University
-* 
+*
 *    Licensed under the Apache License, Version 2.0 (the "License"); you may
 *    not use this file except in compliance with the License. You may obtain
 *    a copy of the License at
@@ -16,10 +16,11 @@
 **/
 
 /**
- * 
+ *
  */
 package net.floodlightcontroller.packet;
 
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import java.util.Arrays;
@@ -52,4 +53,40 @@ public class UDPTest {
         byte[] actual = packet.serialize();
         assertTrue(Arrays.equals(expected, actual));
     }
+
+    @Test
+    public void testDeserializeNotSPUD() throws PacketParsingException {
+        byte[] data = new byte[] {
+                0x04, (byte) 0x89, 0x00, 0x35, 0x00, 0x2C,
+                (byte) 0xAB, (byte) 0xB4, 0x00, 0x01, 0x01,
+                0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+                0x00, 0x00, 0x04, 0x70, 0x6F, 0x70, 0x64,
+                0x02, 0x69, 0x78, 0x06, 0x6E, 0x65, 0x74,
+                0x63, 0x6F, 0x6D, 0x03, 0x63, 0x6F, 0x6D,
+                0x00, 0x00, 0x01, 0x00, 0x01
+        };
+        UDP packet = new UDP();
+        packet.deserialize(data, 0, data.length);
+        IPacket thePayload = packet.getPayload();
+        assertFalse(thePayload instanceof SPUD);
+        byte[] packetSerialized = packet.serialize();
+        assertTrue(Arrays.equals(data, packetSerialized));
+    }
+
+    @Test
+    public void testDeserializeSPUD() throws PacketParsingException {
+        byte[] data = new byte[] {
+                (byte) 0xd5, (byte) 0xdf, (byte) 0x98, 0x27,
+                0x00, 0x15, (byte) 0xfe, 0x28, (byte) 0xd8,
+                0x00, 0x00, (byte) 0xd8, (byte) 0xc2, 0x6f,
+                0x7a, 0x7d, 0x56, (byte) 0xa2, (byte) 0xe5,
+                (byte) 0xa8, 0x40
+        };
+        UDP packet = new UDP();
+        packet.deserialize(data, 0, data.length);
+        IPacket thePayload = packet.getPayload();
+        assertTrue(thePayload instanceof SPUD);
+        byte[] packetSerialized = packet.serialize();
+        assertTrue(Arrays.equals(data, packetSerialized));
+    }
 }
diff --git a/src/test/java/net/floodlightcontroller/util/OFMessageDamperMockSwitch.java b/src/test/java/net/floodlightcontroller/util/OFMessageDamperMockSwitch.java
index c11a14372eb33ad7d007e6d75c05bb2d1d68cc73..1416e8353b59fb7b82bf42a5ad95a8d37c2eb10e 100644
--- a/src/test/java/net/floodlightcontroller/util/OFMessageDamperMockSwitch.java
+++ b/src/test/java/net/floodlightcontroller/util/OFMessageDamperMockSwitch.java
@@ -183,10 +183,16 @@ public class OFMessageDamperMockSwitch implements IOFSwitch {
     }
 
     @Override
-    public short getTables() {
+    public short getNumTables() {
         fail("Unexpected method call");
         return 0;
     }
+    
+    @Override
+    public Collection<TableId> getTables() {
+    	fail("Unexpected method call");
+    	return null;
+    }
 
     @Override
     public boolean attributeEquals(String name, Object other) {
diff --git a/src/test/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkFilterTest.java b/src/test/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkFilterTest.java
index 9a90fef2d53ecbe616e64c8f94f172c3998ebd56..3c07e937b3a309833f9e283b69a24441b952522a 100644
--- a/src/test/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkFilterTest.java
+++ b/src/test/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkFilterTest.java
@@ -32,9 +32,11 @@ import org.projectfloodlight.openflow.protocol.OFVersion;
 import org.projectfloodlight.openflow.types.DatapathId;
 import org.projectfloodlight.openflow.types.EthType;
 import org.projectfloodlight.openflow.types.IPv4Address;
+import org.projectfloodlight.openflow.types.IPv6Address;
 import org.projectfloodlight.openflow.types.MacAddress;
 import org.projectfloodlight.openflow.types.OFBufferId;
 import org.projectfloodlight.openflow.types.OFPort;
+import org.projectfloodlight.openflow.types.VlanVid;
 import org.sdnplatform.sync.ISyncService;
 import org.sdnplatform.sync.test.MockSyncService;
 
@@ -357,8 +359,8 @@ public class VirtualNetworkFilterTest extends FloodlightTestCase {
         IFloodlightProviderService.bcStore.put(cntx,
                            IFloodlightProviderService.CONTEXT_PI_PAYLOAD,
                                (Ethernet)mac1ToGwPacketIntestPacket);
-        deviceService.learnEntity(((Ethernet)mac1ToGwPacketIntestPacket).getDestinationMACAddress().getLong(),
-        		null, IPv4.toIPv4Address(gw1), null, null);
+        deviceService.learnEntity(((Ethernet)mac1ToGwPacketIntestPacket).getDestinationMACAddress(),
+        		VlanVid.ZERO, IPv4Address.of(gw1), IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO);
         Command ret = listener.receive(sw1, mac1ToGwPacketIn, cntx);
         assertTrue(ret == Command.CONTINUE);
     }