diff --git a/src/main/java/net/floodlightcontroller/accesscontrollist/ACL.java b/src/main/java/net/floodlightcontroller/accesscontrollist/ACL.java
index b3c2a88f99bdb9836487cc53b6206fe8f100613a..edfc1015f656cde20bf9589091547fd582ed451e 100644
--- a/src/main/java/net/floodlightcontroller/accesscontrollist/ACL.java
+++ b/src/main/java/net/floodlightcontroller/accesscontrollist/ACL.java
@@ -413,6 +413,11 @@ 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) {
 
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/StatsReplySerializer.java b/src/main/java/net/floodlightcontroller/core/web/serializers/StatsReplySerializer.java
index 67474cbab8a5801178a56b5598e980a1f1fc403a..3481e974505c4bcdfe50801a60a40848796c6ecd 100644
--- a/src/main/java/net/floodlightcontroller/core/web/serializers/StatsReplySerializer.java
+++ b/src/main/java/net/floodlightcontroller/core/web/serializers/StatsReplySerializer.java
@@ -689,31 +689,36 @@ public class StatsReplySerializer extends JsonSerializer<StatsReply> {
 		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();
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 dbc20c0bdec4e98bc97b1390df62e5c82093a1bc..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,14 +1274,14 @@ 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) && (arp.getSenderHardwareAddress().equals(dlAddr))) {
@@ -1189,6 +1290,21 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT
 		}
 		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());
@@ -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 = 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 d331fe2af4f4b6f1843470fd9b051fbc02465455..7f07b1a595a9bc7bc6098be628133b81b2adb0f8 100644
--- a/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java
+++ b/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java
@@ -358,7 +358,7 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule {
 				 * since it's a prerequisite for transport ports.
 				 */
 				if (!FLOWMOD_DEFAULT_MATCH_IP_ADDR) {
-					mb.setExact(MatchField.ETH_TYPE, EthType.IPv4);
+					mb.setExact(MatchField.ETH_TYPE, EthType.IPv6);
 				}
 				
 				if (ip.getNextHeader().equals(IpProtocol.TCP)) {
diff --git a/src/main/java/net/floodlightcontroller/packet/Ethernet.java b/src/main/java/net/floodlightcontroller/packet/Ethernet.java
index d9feae28aa1b21cc1ae35ff4585b13e010eadc8e..b3ef406e69298fc785e975cb488fb3833de5d36b 100644
--- a/src/main/java/net/floodlightcontroller/packet/Ethernet.java
+++ b/src/main/java/net/floodlightcontroller/packet/Ethernet.java
@@ -476,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");
         }
@@ -488,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
index d8af941018bd2ad2f8ae0b37f02d0cee7522d29c..e15b45e024fd9a4ecdf6aa9f9e1915a457ab8e5a 100644
--- a/src/main/java/net/floodlightcontroller/packet/IPv6.java
+++ b/src/main/java/net/floodlightcontroller/packet/IPv6.java
@@ -1,255 +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();
-        }
-        // 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 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()); // TODO: U8.f()?
-        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;
-    }
+	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/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/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/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/devicemanager/internal/DeviceManagerImplTest.java b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java
index 116e8bb179633d8afdc9a42be590d317ac58a887..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;
@@ -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);
@@ -1319,7 +1541,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		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 = IPv4Address.of("192.168.1.1").getInt();
-		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/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 a2640a98032f44aaa52c765ca2a01b240ef46816..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;
@@ -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/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);
     }