[ONOS-641] Update unit tests to include IPv6 as well

- HostMonitorTest
    Add more IPv6 related test cases
- HostLocationProviderTest
    Add more test cases, including:
        NS/NA
	RS/RA
	DAD
	IPv6 unicast
	IPv6 multicast
	IPv4
- HostLocationProvider
    Minor indent fix

Change-Id: Id4c3af430862c61872ccaa98beac3a47313206ac
diff --git a/core/net/src/test/java/org/onosproject/net/host/impl/HostMonitorTest.java b/core/net/src/test/java/org/onosproject/net/host/impl/HostMonitorTest.java
index d167197..3cd2ca2 100644
--- a/core/net/src/test/java/org/onosproject/net/host/impl/HostMonitorTest.java
+++ b/core/net/src/test/java/org/onosproject/net/host/impl/HostMonitorTest.java
@@ -23,10 +23,12 @@
 import org.junit.Test;
 import org.onlab.packet.ARP;
 import org.onlab.packet.Ethernet;
+import org.onlab.packet.IPv6;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
+import org.onlab.packet.ndp.NeighborSolicitation;
 import org.onosproject.incubator.net.intf.Interface;
 import org.onosproject.incubator.net.intf.InterfaceService;
 import org.onosproject.net.ConnectPoint;
@@ -64,14 +66,22 @@
 
 public class HostMonitorTest {
 
-    private static final IpAddress TARGET_IP_ADDR =
+    private static final IpAddress TARGET_IPV4_ADDR =
             IpAddress.valueOf("10.0.0.1");
-    private static final IpAddress SOURCE_ADDR =
+    private static final IpAddress SOURCE_IPV4_ADDR =
             IpAddress.valueOf("10.0.0.99");
     private static final InterfaceIpAddress IA1 =
-            new InterfaceIpAddress(SOURCE_ADDR, IpPrefix.valueOf("10.0.0.0/24"));
+            new InterfaceIpAddress(SOURCE_IPV4_ADDR, IpPrefix.valueOf("10.0.0.0/24"));
     private MacAddress sourceMac = MacAddress.valueOf(1L);
 
+    private static final IpAddress TARGET_IPV6_ADDR =
+            IpAddress.valueOf("1000::1");
+    private static final IpAddress SOURCE_IPV6_ADDR =
+            IpAddress.valueOf("1000::f");
+    private static final InterfaceIpAddress IA2 =
+            new InterfaceIpAddress(SOURCE_IPV6_ADDR, IpPrefix.valueOf("1000::/64"));
+    private MacAddress sourceMac2 = MacAddress.valueOf(2L);
+
     private EdgePortService edgePortService;
 
     private HostMonitor hostMonitor;
@@ -90,7 +100,7 @@
     }
 
     @Test
-    public void testMonitorHostExists() throws Exception {
+    public void testMonitorIpv4HostExists() throws Exception {
         ProviderId id = new ProviderId("fake://", "id");
 
         Host host = createMock(Host.class);
@@ -98,7 +108,7 @@
         replay(host);
 
         HostManager hostManager = createMock(HostManager.class);
-        expect(hostManager.getHostsByIp(TARGET_IP_ADDR))
+        expect(hostManager.getHostsByIp(TARGET_IPV4_ADDR))
                 .andReturn(Collections.singleton(host));
         replay(hostManager);
 
@@ -111,7 +121,7 @@
         hostMonitor = new HostMonitor(null, hostManager, null, edgePortService);
 
         hostMonitor.registerHostProvider(hostProvider);
-        hostMonitor.addMonitoringFor(TARGET_IP_ADDR);
+        hostMonitor.addMonitoringFor(TARGET_IPV4_ADDR);
 
         hostMonitor.run(null);
 
@@ -119,7 +129,36 @@
     }
 
     @Test
-    public void testMonitorHostDoesNotExist() throws Exception {
+    public void testMonitorIpv6HostExists() throws Exception {
+        ProviderId id = new ProviderId("fake://", "id");
+
+        Host host = createMock(Host.class);
+        expect(host.providerId()).andReturn(id);
+        replay(host);
+
+        HostManager hostManager = createMock(HostManager.class);
+        expect(hostManager.getHostsByIp(TARGET_IPV6_ADDR))
+                .andReturn(Collections.singleton(host));
+        replay(hostManager);
+
+        HostProvider hostProvider = createMock(HostProvider.class);
+        expect(hostProvider.id()).andReturn(id).anyTimes();
+        hostProvider.triggerProbe(host);
+        expectLastCall().once();
+        replay(hostProvider);
+
+        hostMonitor = new HostMonitor(null, hostManager, null, edgePortService);
+
+        hostMonitor.registerHostProvider(hostProvider);
+        hostMonitor.addMonitoringFor(TARGET_IPV6_ADDR);
+
+        hostMonitor.run(null);
+
+        verify(hostProvider);
+    }
+
+    @Test
+    public void testMonitorIpv4HostDoesNotExist() throws Exception {
 
         HostManager hostManager = createMock(HostManager.class);
 
@@ -140,12 +179,12 @@
 
         ConnectPoint cp = new ConnectPoint(devId, portNum);
 
-        expect(hostManager.getHostsByIp(TARGET_IP_ADDR))
+        expect(hostManager.getHostsByIp(TARGET_IPV4_ADDR))
                 .andReturn(Collections.emptySet()).anyTimes();
         replay(hostManager);
 
         InterfaceService interfaceService = createMock(InterfaceService.class);
-        expect(interfaceService.getMatchingInterface(TARGET_IP_ADDR))
+        expect(interfaceService.getMatchingInterface(TARGET_IPV4_ADDR))
                 .andReturn(new Interface(cp, Collections.singleton(IA1), sourceMac, VlanId.NONE))
                 .anyTimes();
         replay(interfaceService);
@@ -156,7 +195,7 @@
         // Run the test
         hostMonitor = new HostMonitor(packetService, hostManager, interfaceService, edgePortService);
 
-        hostMonitor.addMonitoringFor(TARGET_IP_ADDR);
+        hostMonitor.addMonitoringFor(TARGET_IPV4_ADDR);
         hostMonitor.run(null);
 
 
@@ -178,16 +217,85 @@
         Ethernet eth = Ethernet.deserializer().deserialize(pktData, 0, pktData.length);
         assertEquals(Ethernet.VLAN_UNTAGGED, eth.getVlanID());
         ARP arp = (ARP) eth.getPayload();
-        assertArrayEquals(SOURCE_ADDR.toOctets(),
+        assertArrayEquals(SOURCE_IPV4_ADDR.toOctets(),
                           arp.getSenderProtocolAddress());
         assertArrayEquals(sourceMac.toBytes(),
                           arp.getSenderHardwareAddress());
-        assertArrayEquals(TARGET_IP_ADDR.toOctets(),
+        assertArrayEquals(TARGET_IPV4_ADDR.toOctets(),
                           arp.getTargetProtocolAddress());
     }
 
     @Test
-    public void testMonitorHostDoesNotExistWithVlan() throws Exception {
+    public void testMonitorIpv6HostDoesNotExist() throws Exception {
+
+        HostManager hostManager = createMock(HostManager.class);
+
+        DeviceId devId = DeviceId.deviceId("fake");
+
+        Device device = createMock(Device.class);
+        expect(device.id()).andReturn(devId).anyTimes();
+        replay(device);
+
+        PortNumber portNum = PortNumber.portNumber(2L);
+
+        Port port = createMock(Port.class);
+        expect(port.number()).andReturn(portNum).anyTimes();
+        replay(port);
+
+        TestDeviceService deviceService = new TestDeviceService();
+        deviceService.addDevice(device, Collections.singleton(port));
+
+        ConnectPoint cp = new ConnectPoint(devId, portNum);
+
+        expect(hostManager.getHostsByIp(TARGET_IPV6_ADDR))
+                .andReturn(Collections.emptySet()).anyTimes();
+        replay(hostManager);
+
+        InterfaceService interfaceService = createMock(InterfaceService.class);
+        expect(interfaceService.getMatchingInterface(TARGET_IPV6_ADDR))
+                .andReturn(new Interface(cp, Collections.singleton(IA2), sourceMac2, VlanId.NONE))
+                .anyTimes();
+        replay(interfaceService);
+
+        TestPacketService packetService = new TestPacketService();
+
+
+        // Run the test
+        hostMonitor = new HostMonitor(packetService, hostManager, interfaceService, edgePortService);
+
+        hostMonitor.addMonitoringFor(TARGET_IPV6_ADDR);
+        hostMonitor.run(null);
+
+
+        // Check that a packet was sent to our PacketService and that it has
+        // the properties we expect
+        assertEquals(1, packetService.packets.size());
+        OutboundPacket packet = packetService.packets.get(0);
+
+        // Check the output port is correct
+        assertEquals(1, packet.treatment().immediate().size());
+        Instruction instruction = packet.treatment().immediate().get(0);
+        assertTrue(instruction instanceof OutputInstruction);
+        OutputInstruction oi = (OutputInstruction) instruction;
+        assertEquals(portNum, oi.port());
+
+        // Check the output packet is correct (well the important bits anyway)
+        final byte[] pktData = new byte[packet.data().remaining()];
+        packet.data().get(pktData);
+        Ethernet eth = Ethernet.deserializer().deserialize(pktData, 0, pktData.length);
+        assertEquals(Ethernet.VLAN_UNTAGGED, eth.getVlanID());
+        IPv6 ipv6 = (IPv6) eth.getPayload();
+        assertArrayEquals(SOURCE_IPV6_ADDR.toOctets(), ipv6.getSourceAddress());
+
+        NeighborSolicitation ns =
+                (NeighborSolicitation) ipv6.getPayload().getPayload();
+        assertArrayEquals(sourceMac2.toBytes(), ns.getOptions().get(0).data());
+
+        assertArrayEquals(TARGET_IPV6_ADDR.toOctets(), ns.getTargetAddress());
+    }
+
+    @Test
+    public void testMonitorIpv4HostDoesNotExistWithVlan() throws Exception {
 
         HostManager hostManager = createMock(HostManager.class);
 
@@ -209,12 +317,12 @@
 
         ConnectPoint cp = new ConnectPoint(devId, portNum);
 
-        expect(hostManager.getHostsByIp(TARGET_IP_ADDR))
+        expect(hostManager.getHostsByIp(TARGET_IPV4_ADDR))
                 .andReturn(Collections.emptySet()).anyTimes();
         replay(hostManager);
 
         InterfaceService interfaceService = createMock(InterfaceService.class);
-        expect(interfaceService.getMatchingInterface(TARGET_IP_ADDR))
+        expect(interfaceService.getMatchingInterface(TARGET_IPV4_ADDR))
                 .andReturn(new Interface(cp, Collections.singleton(IA1), sourceMac, VlanId.vlanId(vlan)))
                 .anyTimes();
         replay(interfaceService);
@@ -225,7 +333,7 @@
         // Run the test
         hostMonitor = new HostMonitor(packetService, hostManager, interfaceService, edgePortService);
 
-        hostMonitor.addMonitoringFor(TARGET_IP_ADDR);
+        hostMonitor.addMonitoringFor(TARGET_IPV4_ADDR);
         hostMonitor.run(null);
 
 
@@ -247,14 +355,84 @@
         Ethernet eth = Ethernet.deserializer().deserialize(pktData, 0, pktData.length);
         assertEquals(vlan, eth.getVlanID());
         ARP arp = (ARP) eth.getPayload();
-        assertArrayEquals(SOURCE_ADDR.toOctets(),
+        assertArrayEquals(SOURCE_IPV4_ADDR.toOctets(),
                           arp.getSenderProtocolAddress());
         assertArrayEquals(sourceMac.toBytes(),
                           arp.getSenderHardwareAddress());
-        assertArrayEquals(TARGET_IP_ADDR.toOctets(),
+        assertArrayEquals(TARGET_IPV4_ADDR.toOctets(),
                           arp.getTargetProtocolAddress());
     }
 
+    @Test
+    public void testMonitorIpv6HostDoesNotExistWithVlan() throws Exception {
+
+        HostManager hostManager = createMock(HostManager.class);
+
+        DeviceId devId = DeviceId.deviceId("fake");
+        short vlan = 5;
+
+        Device device = createMock(Device.class);
+        expect(device.id()).andReturn(devId).anyTimes();
+        replay(device);
+
+        PortNumber portNum = PortNumber.portNumber(1L);
+
+        Port port = createMock(Port.class);
+        expect(port.number()).andReturn(portNum).anyTimes();
+        replay(port);
+
+        TestDeviceService deviceService = new TestDeviceService();
+        deviceService.addDevice(device, Collections.singleton(port));
+
+        ConnectPoint cp = new ConnectPoint(devId, portNum);
+
+        expect(hostManager.getHostsByIp(TARGET_IPV6_ADDR))
+                .andReturn(Collections.emptySet()).anyTimes();
+        replay(hostManager);
+
+        InterfaceService interfaceService = createMock(InterfaceService.class);
+        expect(interfaceService.getMatchingInterface(TARGET_IPV6_ADDR))
+                .andReturn(new Interface(cp, Collections.singleton(IA2), sourceMac2, VlanId.vlanId(vlan)))
+                .anyTimes();
+        replay(interfaceService);
+
+        TestPacketService packetService = new TestPacketService();
+
+
+        // Run the test
+        hostMonitor = new HostMonitor(packetService, hostManager, interfaceService, edgePortService);
+
+        hostMonitor.addMonitoringFor(TARGET_IPV6_ADDR);
+        hostMonitor.run(null);
+
+
+        // Check that a packet was sent to our PacketService and that it has
+        // the properties we expect
+        assertEquals(1, packetService.packets.size());
+        OutboundPacket packet = packetService.packets.get(0);
+
+        // Check the output port is correct
+        assertEquals(1, packet.treatment().immediate().size());
+        Instruction instruction = packet.treatment().immediate().get(0);
+        assertTrue(instruction instanceof OutputInstruction);
+        OutputInstruction oi = (OutputInstruction) instruction;
+        assertEquals(portNum, oi.port());
+
+        // Check the output packet is correct (well the important bits anyway)
+        final byte[] pktData = new byte[packet.data().remaining()];
+        packet.data().get(pktData);
+        Ethernet eth = Ethernet.deserializer().deserialize(pktData, 0, pktData.length);
+        assertEquals(vlan, eth.getVlanID());
+        IPv6 ipv6 = (IPv6) eth.getPayload();
+        assertArrayEquals(SOURCE_IPV6_ADDR.toOctets(), ipv6.getSourceAddress());
+
+        NeighborSolicitation ns =
+                (NeighborSolicitation) ipv6.getPayload().getPayload();
+        assertArrayEquals(sourceMac2.toBytes(), ns.getOptions().get(0).data());
+
+        assertArrayEquals(TARGET_IPV6_ADDR.toOctets(), ns.getTargetAddress());
+    }
+
     class TestPacketService extends PacketServiceAdapter {
 
         List<OutboundPacket> packets = new ArrayList<>();
diff --git a/providers/host/src/main/java/org/onosproject/provider/host/impl/HostLocationProvider.java b/providers/host/src/main/java/org/onosproject/provider/host/impl/HostLocationProvider.java
index 9a82363..93f6bf8 100644
--- a/providers/host/src/main/java/org/onosproject/provider/host/impl/HostLocationProvider.java
+++ b/providers/host/src/main/java/org/onosproject/provider/host/impl/HostLocationProvider.java
@@ -335,15 +335,15 @@
                                                  arp.getSenderProtocolAddress());
                 updateLocationIP(hid, srcMac, vlan, hloc, ip);
 
-                // IPv4: update location only
+            // IPv4: update location only
             } else if (eth.getEtherType() == Ethernet.TYPE_IPV4) {
                 updateLocation(hid, srcMac, vlan, hloc);
 
-                //
-                // NeighborAdvertisement and NeighborSolicitation: possible
-                // new hosts, update both location and IP.
-                //
-                // IPv6: update location only
+            //
+            // NeighborAdvertisement and NeighborSolicitation: possible
+            // new hosts, update both location and IP.
+            //
+            // IPv6: update location only
             } else if (eth.getEtherType() == Ethernet.TYPE_IPV6) {
                 IPv6 ipv6 = (IPv6) eth.getPayload();
                 IpAddress ip = IpAddress.valueOf(IpAddress.Version.INET6,
diff --git a/providers/host/src/test/java/org/onosproject/provider/host/impl/HostLocationProviderTest.java b/providers/host/src/test/java/org/onosproject/provider/host/impl/HostLocationProviderTest.java
index db1eb0f..6cbb623 100644
--- a/providers/host/src/test/java/org/onosproject/provider/host/impl/HostLocationProviderTest.java
+++ b/providers/host/src/test/java/org/onosproject/provider/host/impl/HostLocationProviderTest.java
@@ -23,9 +23,17 @@
 import org.onlab.packet.ARP;
 import org.onlab.packet.ChassisId;
 import org.onlab.packet.Ethernet;
+import org.onlab.packet.ICMP6;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.IPv6;
+import org.onlab.packet.Ip6Address;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
+import org.onlab.packet.ndp.NeighborAdvertisement;
+import org.onlab.packet.ndp.NeighborSolicitation;
+import org.onlab.packet.ndp.RouterAdvertisement;
+import org.onlab.packet.ndp.RouterSolicitation;
 import org.onosproject.cfg.ComponentConfigAdapter;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
@@ -66,6 +74,7 @@
 import java.util.Set;
 
 import static org.easymock.EasyMock.*;
+import static org.hamcrest.Matchers.is;
 import static org.junit.Assert.*;
 import static org.onlab.packet.VlanId.vlanId;
 import static org.onosproject.net.Device.Type.SWITCH;
@@ -75,27 +84,42 @@
 import static org.onosproject.net.device.DeviceEvent.Type.*;
 
 public class HostLocationProviderTest {
-
     private static final Integer INPORT = 10;
     private static final String DEV1 = "of:1";
     private static final String DEV2 = "of:2";
     private static final String DEV3 = "of:3";
+    private static final String DEV4 = "of:4";
+    private static final String DEV5 = "of:5";
+    private static final String DEV6 = "of:6";
 
     private static final VlanId VLAN = vlanId();
+
+    // IPv4 Host
     private static final MacAddress MAC = MacAddress.valueOf("00:00:11:00:00:01");
     private static final MacAddress BCMAC = MacAddress.valueOf("ff:ff:ff:ff:ff:ff");
     private static final byte[] IP = new byte[]{10, 0, 0, 1};
-
     private static final IpAddress IP_ADDRESS =
             IpAddress.valueOf(IpAddress.Version.INET, IP);
     private static final HostLocation LOCATION =
             new HostLocation(deviceId(DEV1), portNumber(INPORT), 0L);
-
     private static final DefaultHost HOST =
             new DefaultHost(ProviderId.NONE, hostId(MAC), MAC,
                             vlanId(VlanId.UNTAGGED), LOCATION,
                             ImmutableSet.of(IP_ADDRESS));
 
+    // IPv6 Host
+    private static final MacAddress MAC2 = MacAddress.valueOf("00:00:22:00:00:02");
+    private static final MacAddress BCMAC2 = MacAddress.valueOf("33:33:00:00:00:01");
+    private static final byte[] IP2 = Ip6Address.valueOf("1000::1").toOctets();
+    private static final IpAddress IP_ADDRESS2 =
+            IpAddress.valueOf(IpAddress.Version.INET6, IP2);
+    private static final HostLocation LOCATION2 =
+            new HostLocation(deviceId(DEV4), portNumber(INPORT), 0L);
+    private static final DefaultHost HOST2 =
+            new DefaultHost(ProviderId.NONE, hostId(MAC2), MAC2,
+                            vlanId(VlanId.UNTAGGED), LOCATION2,
+                            ImmutableSet.of(IP_ADDRESS2));
+
     private static final ComponentContextAdapter CTX_FOR_REMOVE =
             new ComponentContextAdapter() {
                 @Override
@@ -157,51 +181,189 @@
     @Test
     public void events() {
         // new host
-        testProcessor.process(new TestPacketContext(DEV1));
+        testProcessor.process(new TestArpPacketContext(DEV1));
         assertNotNull("new host expected", providerService.added);
         assertNull("host motion unexpected", providerService.moved);
 
         // the host moved to new switch
-        testProcessor.process(new TestPacketContext(DEV2));
+        testProcessor.process(new TestArpPacketContext(DEV2));
         assertNotNull("host motion expected", providerService.moved);
 
         // the host was misheard on a spine
-        testProcessor.process(new TestPacketContext(DEV3));
+        testProcessor.process(new TestArpPacketContext(DEV3));
+        assertNull("host misheard on spine switch", providerService.spine);
+
+        providerService.clear();
+
+        // new host
+        testProcessor.process(new TestNAPacketContext(DEV4));
+        assertNotNull("new host expected", providerService.added);
+        assertNull("host motion unexpected", providerService.moved);
+
+        // the host moved to new switch
+        testProcessor.process(new TestNAPacketContext(DEV5));
+        assertNotNull("host motion expected", providerService.moved);
+
+        // the host was misheard on a spine
+        testProcessor.process(new TestNAPacketContext(DEV6));
         assertNull("host misheard on spine switch", providerService.spine);
     }
 
     @Test
     public void removeHostByDeviceRemove() {
         provider.modified(CTX_FOR_REMOVE);
-        testProcessor.process(new TestPacketContext(DEV1));
+        testProcessor.process(new TestArpPacketContext(DEV1));
+        testProcessor.process(new TestNAPacketContext(DEV4));
+
         Device device = new DefaultDevice(ProviderId.NONE, deviceId(DEV1), SWITCH,
                                           "m", "h", "s", "n", new ChassisId(0L));
         deviceService.listener.event(new DeviceEvent(DEVICE_REMOVED, device));
         assertEquals("incorrect remove count", 1, providerService.removeCount);
+
+        device = new DefaultDevice(ProviderId.NONE, deviceId(DEV4), SWITCH,
+                                          "m", "h", "s", "n", new ChassisId(0L));
+        deviceService.listener.event(new DeviceEvent(DEVICE_REMOVED, device));
+        assertEquals("incorrect remove count", 2, providerService.removeCount);
     }
 
     @Test
     public void removeHostByDeviceOffline() {
         provider.modified(CTX_FOR_REMOVE);
-        testProcessor.process(new TestPacketContext(DEV1));
+        testProcessor.process(new TestArpPacketContext(DEV1));
+        testProcessor.process(new TestArpPacketContext(DEV4));
+
         Device device = new DefaultDevice(ProviderId.NONE, deviceId(DEV1), SWITCH,
                                           "m", "h", "s", "n", new ChassisId(0L));
         deviceService.listener.event(new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device));
         assertEquals("incorrect remove count", 1, providerService.removeCount);
+
+        device = new DefaultDevice(ProviderId.NONE, deviceId(DEV4), SWITCH,
+                                          "m", "h", "s", "n", new ChassisId(0L));
+        deviceService.listener.event(new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device));
+        assertEquals("incorrect remove count", 2, providerService.removeCount);
     }
 
     @Test
     public void removeHostByDevicePortDown() {
         provider.modified(CTX_FOR_REMOVE);
-        testProcessor.process(new TestPacketContext(DEV1));
+        testProcessor.process(new TestArpPacketContext(DEV1));
+        testProcessor.process(new TestArpPacketContext(DEV4));
+
         Device device = new DefaultDevice(ProviderId.NONE, deviceId(DEV1), SWITCH,
                                           "m", "h", "s", "n", new ChassisId(0L));
         deviceService.listener.event(new DeviceEvent(PORT_UPDATED, device,
-                                                     new DefaultPort(device, portNumber(INPORT),
-                                                                     false)));
+                new DefaultPort(device, portNumber(INPORT), false)));
         assertEquals("incorrect remove count", 1, providerService.removeCount);
+
+        device = new DefaultDevice(ProviderId.NONE, deviceId(DEV4), SWITCH,
+                                          "m", "h", "s", "n", new ChassisId(0L));
+        deviceService.listener.event(new DeviceEvent(PORT_UPDATED, device,
+                new DefaultPort(device, portNumber(INPORT), false)));
+        assertEquals("incorrect remove count", 2, providerService.removeCount);
     }
 
+    /**
+     * When receiving ARP, updates location and IP.
+     */
+    @Test
+    public void testReceiveArp() {
+        testProcessor.process(new TestArpPacketContext(DEV1));
+        HostDescription descr = providerService.added;
+        assertThat(descr.location(), is(LOCATION));
+        assertThat(descr.hwAddress(), is(MAC));
+        assertThat(descr.ipAddress().toArray()[0], is(IP_ADDRESS));
+        assertThat(descr.vlan(), is(VLAN));
+    }
+
+    /**
+     * When receiving IPv4, updates location only.
+     */
+    @Test
+    public void testReceiveIpv4() {
+        testProcessor.process(new TestIpv4PacketContext(DEV1));
+        HostDescription descr = providerService.added;
+        assertThat(descr.location(), is(LOCATION));
+        assertThat(descr.hwAddress(), is(MAC));
+        assertThat(descr.ipAddress().size(), is(0));
+        assertThat(descr.vlan(), is(VLAN));
+    }
+
+    /**
+     * When receiving NeighborAdvertisement, updates location and IP.
+     */
+    @Test
+    public void testReceiveNA() {
+        testProcessor.process(new TestNAPacketContext(DEV4));
+        assertNotNull(providerService.added);
+        HostDescription descr = providerService.added;
+        assertThat(descr.location(), is(LOCATION2));
+        assertThat(descr.hwAddress(), is(MAC2));
+        assertThat(descr.ipAddress().toArray()[0], is(IP_ADDRESS2));
+        assertThat(descr.vlan(), is(VLAN));
+    }
+
+    /**
+     * When receiving NeighborSolicitation, updates location and IP.
+     */
+    @Test
+    public void testReceiveNS() {
+        testProcessor.process(new TestNSPacketContext(DEV4));
+        HostDescription descr = providerService.added;
+        assertThat(descr.location(), is(LOCATION2));
+        assertThat(descr.hwAddress(), is(MAC2));
+        assertThat(descr.ipAddress().toArray()[0], is(IP_ADDRESS2));
+        assertThat(descr.vlan(), is(VLAN));
+    }
+
+    /**
+     * When receiving RouterAdvertisement, ignores it.
+     */
+    @Test
+    public void testReceivesRA() {
+        testProcessor.process(new TestRAPacketContext(DEV4));
+        assertNull(providerService.added);
+    }
+
+    /**
+     * When receiving RouterSolicitation, ignores it.
+     */
+    @Test
+    public void testReceiveRS() {
+        testProcessor.process(new TestRSPacketContext(DEV4));
+        assertNull(providerService.added);
+    }
+
+    /**
+     * When receiving Duplicate Address Detection (DAD), ignores it.
+     */
+    @Test
+    public void testReceiveDAD() {
+        testProcessor.process(new TestDADPacketContext(DEV4));
+        assertNull(providerService.added);
+    }
+
+    /**
+     * When receiving IPv6 multicast packet, ignores it.
+     */
+    @Test
+    public void testReceiveIpv6Multicast() {
+        testProcessor.process(new TestIpv6McastPacketContext(DEV4));
+        assertNull(providerService.added);
+    }
+
+    /**
+     * When receiving IPv6 unicast packet, updates location only.
+     */
+    @Test
+    public void testReceiveIpv6Unicast() {
+        testProcessor.process(new TestIpv6PacketContext(DEV4));
+        assertNotNull(providerService.added);
+        HostDescription descr = providerService.added;
+        assertThat(descr.location(), is(LOCATION2));
+        assertThat(descr.hwAddress(), is(MAC2));
+        assertThat(descr.ipAddress().size(), is(0));
+        assertThat(descr.vlan(), is(VLAN));
+    }
 
     @After
     public void tearDown() {
@@ -233,24 +395,30 @@
             extends AbstractProviderService<HostProvider>
             implements HostProviderService {
 
-        DeviceId added = null;
-        DeviceId moved = null;
-        DeviceId spine = null;
+        HostDescription added = null;
+        HostDescription moved = null;
+        HostDescription spine = null;
         public int removeCount;
 
+        public void clear() {
+            added = null;
+            moved = null;
+            spine = null;
+            removeCount = 0;
+        }
+
         protected TestHostProviderService(HostProvider provider) {
             super(provider);
         }
 
         @Override
         public void hostDetected(HostId hostId, HostDescription hostDescription, boolean replaceIps) {
-            DeviceId descr = hostDescription.location().deviceId();
             if (added == null) {
-                added = descr;
-            } else if ((moved == null) && !descr.equals(added)) {
-                moved = descr;
+                added = hostDescription;
+            } else if ((moved == null) && !hostDescription.equals(added)) {
+                moved = hostDescription;
             } else {
-                spine = descr;
+                spine = hostDescription;
             }
         }
 
@@ -272,24 +440,26 @@
         }
     }
 
-
     private class TestTopologyService extends TopologyServiceAdapter {
         @Override
         public boolean isInfrastructure(Topology topology,
                                         ConnectPoint connectPoint) {
             //simulate DPID3 as an infrastructure switch
-            if ((connectPoint.deviceId()).equals(deviceId(DEV3))) {
+            if ((connectPoint.deviceId()).equals(deviceId(DEV3)) ||
+                    connectPoint.deviceId().equals(deviceId(DEV6))) {
                 return true;
             }
             return false;
         }
     }
 
-    private class TestPacketContext implements PacketContext {
-
+    /**
+     * Generates ARP packet.
+     */
+    private class TestArpPacketContext implements PacketContext {
         private final String deviceId;
 
-        public TestPacketContext(String deviceId) {
+        public TestArpPacketContext(String deviceId) {
             this.deviceId = deviceId;
         }
 
@@ -344,6 +514,490 @@
         }
     }
 
+    /**
+     * Generates IPv6 Unicast packet.
+     */
+    private class TestIpv4PacketContext implements PacketContext {
+        private final String deviceId;
+
+        public TestIpv4PacketContext(String deviceId) {
+            this.deviceId = deviceId;
+        }
+
+        @Override
+        public long time() {
+            return 0;
+        }
+
+        @Override
+        public InboundPacket inPacket() {
+            IPv4 ipv4 = new IPv4();
+            ipv4.setDestinationAddress("10.0.0.1");
+            ipv4.setSourceAddress(IP_ADDRESS.toString());
+            Ethernet eth = new Ethernet();
+            eth.setEtherType(Ethernet.TYPE_IPV4)
+                    .setVlanID(VLAN.toShort())
+                    .setSourceMACAddress(MAC)
+                    .setDestinationMACAddress(MacAddress.valueOf("00:00:00:00:00:01"))
+                    .setPayload(ipv4);
+            ConnectPoint receivedFrom = new ConnectPoint(deviceId(deviceId),
+                                                         portNumber(INPORT));
+            return new DefaultInboundPacket(receivedFrom, eth,
+                                            ByteBuffer.wrap(eth.serialize()));
+        }
+
+        @Override
+        public OutboundPacket outPacket() {
+            return null;
+        }
+
+        @Override
+        public TrafficTreatment.Builder treatmentBuilder() {
+            return null;
+        }
+
+        @Override
+        public void send() {
+
+        }
+
+        @Override
+        public boolean block() {
+            return false;
+        }
+
+        @Override
+        public boolean isHandled() {
+            return false;
+        }
+    }
+
+    /**
+     * Generates NeighborAdvertisement packet.
+     */
+    private class TestNAPacketContext implements PacketContext {
+        private final String deviceId;
+
+        public TestNAPacketContext(String deviceId) {
+            this.deviceId = deviceId;
+        }
+
+        @Override
+        public long time() {
+            return 0;
+        }
+
+        @Override
+        public InboundPacket inPacket() {
+            NeighborAdvertisement na = new NeighborAdvertisement();
+            ICMP6 icmp6 = new ICMP6();
+            icmp6.setPayload(na);
+            IPv6 ipv6 = new IPv6();
+            ipv6.setPayload(icmp6);
+            ipv6.setDestinationAddress(Ip6Address.valueOf("ff02::1").toOctets());
+            ipv6.setSourceAddress(IP2);
+            Ethernet eth = new Ethernet();
+            eth.setEtherType(Ethernet.TYPE_IPV6)
+                    .setVlanID(VLAN.toShort())
+                    .setSourceMACAddress(MAC2.toBytes())
+                    .setDestinationMACAddress(BCMAC2)
+                    .setPayload(ipv6);
+            ConnectPoint receivedFrom = new ConnectPoint(deviceId(deviceId),
+                                                         portNumber(INPORT));
+            return new DefaultInboundPacket(receivedFrom, eth,
+                                            ByteBuffer.wrap(eth.serialize()));
+        }
+
+        @Override
+        public OutboundPacket outPacket() {
+            return null;
+        }
+
+        @Override
+        public TrafficTreatment.Builder treatmentBuilder() {
+            return null;
+        }
+
+        @Override
+        public void send() {
+
+        }
+
+        @Override
+        public boolean block() {
+            return false;
+        }
+
+        @Override
+        public boolean isHandled() {
+            return false;
+        }
+    }
+
+    /**
+     * Generates NeighborSolicitation packet.
+     */
+    private class TestNSPacketContext implements PacketContext {
+        private final String deviceId;
+
+        public TestNSPacketContext(String deviceId) {
+            this.deviceId = deviceId;
+        }
+
+        @Override
+        public long time() {
+            return 0;
+        }
+
+        @Override
+        public InboundPacket inPacket() {
+            NeighborSolicitation ns = new NeighborSolicitation();
+            ICMP6 icmp6 = new ICMP6();
+            icmp6.setPayload(ns);
+            IPv6 ipv6 = new IPv6();
+            ipv6.setPayload(icmp6);
+            ipv6.setDestinationAddress(Ip6Address.valueOf("ff02::1:ff00:0000").toOctets());
+            ipv6.setSourceAddress(IP2);
+            Ethernet eth = new Ethernet();
+            eth.setEtherType(Ethernet.TYPE_IPV6)
+                    .setVlanID(VLAN.toShort())
+                    .setSourceMACAddress(MAC2.toBytes())
+                    .setDestinationMACAddress(BCMAC2)
+                    .setPayload(ipv6);
+            ConnectPoint receivedFrom = new ConnectPoint(deviceId(deviceId),
+                                                         portNumber(INPORT));
+            return new DefaultInboundPacket(receivedFrom, eth,
+                                            ByteBuffer.wrap(eth.serialize()));
+        }
+
+        @Override
+        public OutboundPacket outPacket() {
+            return null;
+        }
+
+        @Override
+        public TrafficTreatment.Builder treatmentBuilder() {
+            return null;
+        }
+
+        @Override
+        public void send() {
+
+        }
+
+        @Override
+        public boolean block() {
+            return false;
+        }
+
+        @Override
+        public boolean isHandled() {
+            return false;
+        }
+    }
+
+    /**
+     * Generates Duplicate Address Detection packet.
+     */
+    private class TestDADPacketContext implements PacketContext {
+        private final String deviceId;
+
+        public TestDADPacketContext(String deviceId) {
+            this.deviceId = deviceId;
+        }
+
+        @Override
+        public long time() {
+            return 0;
+        }
+
+        @Override
+        public InboundPacket inPacket() {
+            NeighborSolicitation ns = new NeighborSolicitation();
+            ICMP6 icmp6 = new ICMP6();
+            icmp6.setPayload(ns);
+            IPv6 ipv6 = new IPv6();
+            ipv6.setPayload(icmp6);
+            ipv6.setDestinationAddress(Ip6Address.valueOf("ff02::1").toOctets());
+            ipv6.setSourceAddress(Ip6Address.valueOf("::").toOctets());
+            Ethernet eth = new Ethernet();
+            eth.setEtherType(Ethernet.TYPE_IPV6)
+                    .setVlanID(VLAN.toShort())
+                    .setSourceMACAddress(MAC2.toBytes())
+                    .setDestinationMACAddress(BCMAC2)
+                    .setPayload(ipv6);
+            ConnectPoint receivedFrom = new ConnectPoint(deviceId(deviceId),
+                                                         portNumber(INPORT));
+            return new DefaultInboundPacket(receivedFrom, eth,
+                                            ByteBuffer.wrap(eth.serialize()));
+        }
+
+        @Override
+        public OutboundPacket outPacket() {
+            return null;
+        }
+
+        @Override
+        public TrafficTreatment.Builder treatmentBuilder() {
+            return null;
+        }
+
+        @Override
+        public void send() {
+
+        }
+
+        @Override
+        public boolean block() {
+            return false;
+        }
+
+        @Override
+        public boolean isHandled() {
+            return false;
+        }
+    }
+
+    /**
+     * Generates Router Solicitation packet.
+     */
+    private class TestRSPacketContext implements PacketContext {
+        private final String deviceId;
+
+        public TestRSPacketContext(String deviceId) {
+            this.deviceId = deviceId;
+        }
+
+        @Override
+        public long time() {
+            return 0;
+        }
+
+        @Override
+        public InboundPacket inPacket() {
+            RouterSolicitation ns = new RouterSolicitation();
+            ICMP6 icmp6 = new ICMP6();
+            icmp6.setPayload(ns);
+            IPv6 ipv6 = new IPv6();
+            ipv6.setPayload(icmp6);
+            ipv6.setDestinationAddress(Ip6Address.valueOf("ff02::2").toOctets());
+            ipv6.setSourceAddress(Ip6Address.valueOf("::").toOctets());
+            Ethernet eth = new Ethernet();
+            eth.setEtherType(Ethernet.TYPE_IPV6)
+                    .setVlanID(VLAN.toShort())
+                    .setSourceMACAddress(MAC2.toBytes())
+                    .setDestinationMACAddress(MacAddress.valueOf("33:33:00:00:00:02"))
+                    .setPayload(ipv6);
+            ConnectPoint receivedFrom = new ConnectPoint(deviceId(deviceId),
+                                                         portNumber(INPORT));
+            return new DefaultInboundPacket(receivedFrom, eth,
+                                            ByteBuffer.wrap(eth.serialize()));
+        }
+
+        @Override
+        public OutboundPacket outPacket() {
+            return null;
+        }
+
+        @Override
+        public TrafficTreatment.Builder treatmentBuilder() {
+            return null;
+        }
+
+        @Override
+        public void send() {
+
+        }
+
+        @Override
+        public boolean block() {
+            return false;
+        }
+
+        @Override
+        public boolean isHandled() {
+            return false;
+        }
+    }
+
+    /**
+     * Generates Router Advertisement packet.
+     */
+    private class TestRAPacketContext implements PacketContext {
+        private final String deviceId;
+
+        public TestRAPacketContext(String deviceId) {
+            this.deviceId = deviceId;
+        }
+
+        @Override
+        public long time() {
+            return 0;
+        }
+
+        @Override
+        public InboundPacket inPacket() {
+            RouterAdvertisement ns = new RouterAdvertisement();
+            ICMP6 icmp6 = new ICMP6();
+            icmp6.setPayload(ns);
+            IPv6 ipv6 = new IPv6();
+            ipv6.setPayload(icmp6);
+            ipv6.setDestinationAddress(Ip6Address.valueOf("ff02::1").toOctets());
+            ipv6.setSourceAddress(IP2);
+            Ethernet eth = new Ethernet();
+            eth.setEtherType(Ethernet.TYPE_IPV6)
+                    .setVlanID(VLAN.toShort())
+                    .setSourceMACAddress(MAC2.toBytes())
+                    .setDestinationMACAddress(MacAddress.valueOf("33:33:00:00:00:01"))
+                    .setPayload(ipv6);
+            ConnectPoint receivedFrom = new ConnectPoint(deviceId(deviceId),
+                                                         portNumber(INPORT));
+            return new DefaultInboundPacket(receivedFrom, eth,
+                                            ByteBuffer.wrap(eth.serialize()));
+        }
+
+        @Override
+        public OutboundPacket outPacket() {
+            return null;
+        }
+
+        @Override
+        public TrafficTreatment.Builder treatmentBuilder() {
+            return null;
+        }
+
+        @Override
+        public void send() {
+
+        }
+
+        @Override
+        public boolean block() {
+            return false;
+        }
+
+        @Override
+        public boolean isHandled() {
+            return false;
+        }
+    }
+
+    /**
+     * Generates IPv6 Multicast packet.
+     */
+    private class TestIpv6McastPacketContext implements PacketContext {
+        private final String deviceId;
+
+        public TestIpv6McastPacketContext(String deviceId) {
+            this.deviceId = deviceId;
+        }
+
+        @Override
+        public long time() {
+            return 0;
+        }
+
+        @Override
+        public InboundPacket inPacket() {
+            IPv6 ipv6 = new IPv6();
+            ipv6.setDestinationAddress(Ip6Address.valueOf("ff02::1").toOctets());
+            ipv6.setSourceAddress(IP2);
+            Ethernet eth = new Ethernet();
+            eth.setEtherType(Ethernet.TYPE_IPV6)
+                    .setVlanID(VLAN.toShort())
+                    .setSourceMACAddress(MAC2.toBytes())
+                    .setDestinationMACAddress(MacAddress.valueOf("33:33:00:00:00:01"))
+                    .setPayload(ipv6);
+            ConnectPoint receivedFrom = new ConnectPoint(deviceId(deviceId),
+                                                         portNumber(INPORT));
+            return new DefaultInboundPacket(receivedFrom, eth,
+                                            ByteBuffer.wrap(eth.serialize()));
+        }
+
+        @Override
+        public OutboundPacket outPacket() {
+            return null;
+        }
+
+        @Override
+        public TrafficTreatment.Builder treatmentBuilder() {
+            return null;
+        }
+
+        @Override
+        public void send() {
+
+        }
+
+        @Override
+        public boolean block() {
+            return false;
+        }
+
+        @Override
+        public boolean isHandled() {
+            return false;
+        }
+    }
+
+    /**
+     * Generates IPv6 Unicast packet.
+     */
+    private class TestIpv6PacketContext implements PacketContext {
+        private final String deviceId;
+
+        public TestIpv6PacketContext(String deviceId) {
+            this.deviceId = deviceId;
+        }
+
+        @Override
+        public long time() {
+            return 0;
+        }
+
+        @Override
+        public InboundPacket inPacket() {
+            IPv6 ipv6 = new IPv6();
+            ipv6.setDestinationAddress(Ip6Address.valueOf("1000::1").toOctets());
+            ipv6.setSourceAddress(IP2);
+            Ethernet eth = new Ethernet();
+            eth.setEtherType(Ethernet.TYPE_IPV6)
+                    .setVlanID(VLAN.toShort())
+                    .setSourceMACAddress(MAC2)
+                    .setDestinationMACAddress(MacAddress.valueOf("00:00:00:00:00:01"))
+                    .setPayload(ipv6);
+            ConnectPoint receivedFrom = new ConnectPoint(deviceId(deviceId),
+                                                         portNumber(INPORT));
+            return new DefaultInboundPacket(receivedFrom, eth,
+                                            ByteBuffer.wrap(eth.serialize()));
+        }
+
+        @Override
+        public OutboundPacket outPacket() {
+            return null;
+        }
+
+        @Override
+        public TrafficTreatment.Builder treatmentBuilder() {
+            return null;
+        }
+
+        @Override
+        public void send() {
+
+        }
+
+        @Override
+        public boolean block() {
+            return false;
+        }
+
+        @Override
+        public boolean isHandled() {
+            return false;
+        }
+    }
+
     private class TestDeviceService extends DeviceServiceAdapter {
         private DeviceListener listener;
 
@@ -361,12 +1015,26 @@
     private class TestHostService extends HostServiceAdapter {
         @Override
         public Set<Host> getConnectedHosts(ConnectPoint connectPoint) {
-            return ImmutableSet.of(HOST);
+            ConnectPoint cp1 = new ConnectPoint(deviceId(DEV1), portNumber(INPORT));
+            ConnectPoint cp2 = new ConnectPoint(deviceId(DEV4), portNumber(INPORT));
+            if (connectPoint.equals(cp1)) {
+                return ImmutableSet.of(HOST);
+            } else if (connectPoint.equals(cp2)) {
+                return ImmutableSet.of(HOST2);
+            } else {
+                return ImmutableSet.of();
+            }
         }
 
         @Override
         public Set<Host> getConnectedHosts(DeviceId deviceId) {
-            return ImmutableSet.of(HOST);
+            if (deviceId.equals(deviceId(DEV1))) {
+                return ImmutableSet.of(HOST);
+            } else if (deviceId.equals(deviceId(DEV4))) {
+                return ImmutableSet.of(HOST2);
+            } else {
+                return ImmutableSet.of();
+            }
         }
 
     }