[CORD-1887] Make DHCP relay works with dual homing

Change-Id: I479a292d6d6d4820798ed660cc51826c3b12ede3
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp4HandlerImpl.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp4HandlerImpl.java
index ccdb407..235b4c5 100644
--- a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp4HandlerImpl.java
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp4HandlerImpl.java
@@ -44,8 +44,12 @@
 import org.onosproject.dhcprelay.store.DhcpRelayStore;
 import org.onosproject.net.host.HostEvent;
 import org.onosproject.net.host.HostListener;
+import org.onosproject.net.host.HostProvider;
+import org.onosproject.net.host.HostProviderRegistry;
+import org.onosproject.net.host.HostProviderService;
 import org.onosproject.net.intf.Interface;
 import org.onosproject.net.intf.InterfaceService;
+import org.onosproject.net.provider.ProviderId;
 import org.onosproject.routeservice.Route;
 import org.onosproject.routeservice.RouteStore;
 import org.onosproject.net.ConnectPoint;
@@ -57,7 +61,6 @@
 import org.onosproject.net.host.DefaultHostDescription;
 import org.onosproject.net.host.HostDescription;
 import org.onosproject.net.host.HostService;
-import org.onosproject.net.host.HostStore;
 import org.onosproject.net.host.InterfaceIpAddress;
 import org.onosproject.net.packet.DefaultOutboundPacket;
 import org.onosproject.net.packet.OutboundPacket;
@@ -85,7 +88,7 @@
 @Component
 @Service
 @Property(name = "version", value = "4")
-public class Dhcp4HandlerImpl implements DhcpHandler {
+public class Dhcp4HandlerImpl implements DhcpHandler, HostProvider {
     private static Logger log = LoggerFactory.getLogger(Dhcp4HandlerImpl.class);
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
@@ -95,9 +98,6 @@
     protected PacketService packetService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected HostStore hostStore;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected RouteStore routeStore;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
@@ -106,6 +106,10 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected HostService hostService;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected HostProviderRegistry providerRegistry;
+
+    protected HostProviderService providerService;
     private InternalHostListener hostListener = new InternalHostListener();
 
     private Ip4Address dhcpServerIp = null;
@@ -130,10 +134,16 @@
     @Activate
     protected void activate() {
         hostService.addListener(hostListener);
+        providerService = providerRegistry.register(this);
     }
 
     @Deactivate
     protected void deactivate() {
+        providerRegistry.unregister(this);
+        hostService.removeListener(hostListener);
+        this.dhcpConnectMac = null;
+        this.dhcpConnectVlan = null;
+
         if (dhcpGatewayIp != null) {
             hostService.stopMonitoringIp(dhcpGatewayIp);
         }
@@ -337,11 +347,6 @@
         checkNotNull(incomingPacketType, "Can't get message type from DHCP payload {}", dhcpPayload);
         switch (incomingPacketType) {
             case DHCPDISCOVER:
-                // Try update host if it is directly connected.
-                if (directlyConnected(dhcpPayload)) {
-                    updateHost(context, dhcpPayload);
-                }
-
                 // Add the gateway IP as virtual interface IP for server to understand
                 // the lease to be assigned and forward the packet to dhcp server.
                 Ethernet ethernetPacketDiscover =
@@ -387,22 +392,6 @@
     }
 
     /**
-     * Updates host to host store according to DHCP payload.
-     *
-     * @param context the packet context
-     * @param dhcpPayload the DHCP payload
-     */
-    private void updateHost(PacketContext context, DHCP dhcpPayload) {
-        ConnectPoint location = context.inPacket().receivedFrom();
-        HostLocation hostLocation = new HostLocation(location, System.currentTimeMillis());
-        MacAddress macAddress = MacAddress.valueOf(dhcpPayload.getClientHardwareAddress());
-        VlanId vlanId = VlanId.vlanId(context.inPacket().parsed().getVlanID());
-        HostId hostId = HostId.hostId(macAddress, vlanId);
-        HostDescription desc = new DefaultHostDescription(macAddress, vlanId, hostLocation);
-        hostStore.createOrUpdateHost(DhcpRelayManager.PROVIDER_ID, hostId, desc, false);
-    }
-
-    /**
      * Checks if this app has been configured.
      *
      * @return true if all information we need have been initialized
@@ -847,11 +836,18 @@
         if (directlyConnected(dhcpPayload)) {
             // Add to host store if it connect to network directly
             Set<IpAddress> ips = Sets.newHashSet(ip);
-            HostDescription desc = new DefaultHostDescription(macAddress, vlanId,
-                                                              hostLocation, ips);
+            Host host = hostService.getHost(hostId);
 
-            // Replace the ip when dhcp server give the host new ip address
-            hostStore.createOrUpdateHost(DhcpRelayManager.PROVIDER_ID, hostId, desc, false);
+            Set<HostLocation> hostLocations = Sets.newHashSet(hostLocation);
+            if (host != null) {
+                // Dual homing support:
+                // if host exists, use old locations and new location
+                hostLocations.addAll(host.locations());
+            }
+            HostDescription desc = new DefaultHostDescription(macAddress, vlanId,
+                                                              hostLocations, ips, false);
+            // Add IP address when dhcp server give the host new ip address
+            providerService.hostDetected(hostId, desc, false);
         } else {
             // Add to route store if it does not connect to network directly
             // Get gateway host IP according to host mac address
@@ -1010,6 +1006,16 @@
         packetService.emit(o);
     }
 
+    @Override
+    public void triggerProbe(Host host) {
+        // Do nothing here
+    }
+
+    @Override
+    public ProviderId id() {
+        return DhcpRelayManager.PROVIDER_ID;
+    }
+
     class InternalHostListener implements HostListener {
         @Override
         public void event(HostEvent event) {
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp6HandlerImpl.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp6HandlerImpl.java
index 27d59d9..c247b90 100644
--- a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp6HandlerImpl.java
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp6HandlerImpl.java
@@ -48,7 +48,9 @@
 import org.onlab.util.HexString;
 import org.onosproject.dhcprelay.api.DhcpHandler;
 import org.onosproject.dhcprelay.store.DhcpRelayStore;
-import org.onosproject.net.host.HostStore;
+import org.onosproject.net.host.HostProvider;
+import org.onosproject.net.host.HostProviderRegistry;
+import org.onosproject.net.host.HostProviderService;
 import org.onosproject.net.host.HostService;
 import org.onosproject.net.host.DefaultHostDescription;
 import org.onosproject.net.host.HostDescription;
@@ -57,6 +59,7 @@
 import org.onosproject.net.host.HostEvent;
 import org.onosproject.net.intf.Interface;
 import org.onosproject.net.intf.InterfaceService;
+import org.onosproject.net.provider.ProviderId;
 import org.onosproject.routeservice.Route;
 import org.onosproject.routeservice.RouteStore;
 import org.onosproject.dhcprelay.config.DhcpServerConfig;
@@ -88,7 +91,7 @@
 @Component
 @Service
 @Property(name = "version", value = "6")
-public class Dhcp6HandlerImpl implements DhcpHandler {
+public class Dhcp6HandlerImpl implements DhcpHandler, HostProvider {
     private static Logger log = LoggerFactory.getLogger(Dhcp6HandlerImpl.class);
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
@@ -98,9 +101,6 @@
     protected PacketService packetService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected HostStore hostStore;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected RouteStore routeStore;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
@@ -109,8 +109,11 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected HostService hostService;
 
-    private InternalHostListener hostListener = new InternalHostListener();
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected HostProviderRegistry providerRegistry;
 
+    private InternalHostListener hostListener = new InternalHostListener();
+    protected HostProviderService providerService;
     private Ip6Address dhcpServerIp = null;
     // dhcp server may be connected directly to the SDN network or
     // via an external gateway. When connected directly, the dhcpConnectPoint, dhcpConnectMac,
@@ -146,11 +149,13 @@
 
     @Activate
     protected void activate() {
+        providerService = providerRegistry.register(this);
         hostService.addListener(hostListener);
     }
 
     @Deactivate
     protected void deactivate() {
+        providerRegistry.unregister(this);
         hostService.removeListener(hostListener);
         this.dhcpConnectMac = null;
         this.dhcpConnectVlan = null;
@@ -162,7 +167,6 @@
         }
     }
 
-
     @Override
     public void setDhcpServerIp(IpAddress dhcpServerIp) {
         checkNotNull(dhcpServerIp, "DHCP server IP can't be null");
@@ -322,6 +326,16 @@
         return this.dhcpServerConnectPoint != null && this.dhcpServerIp != null;
     }
 
+    @Override
+    public ProviderId id() {
+        return DhcpRelayManager.PROVIDER_ID;
+    }
+
+    @Override
+    public void triggerProbe(Host host) {
+        // Do nothing here
+    }
+
     // the new class the contains Ethernet packet and destination port, kind of like adding
     // internal header to the packet
     private class InternalPacket {
@@ -631,9 +645,8 @@
 
                     log.debug("client mac {} client vlan {}", HexString.toHexString(clientMac.toBytes(), ":"), vlanId);
 
-
                     // Remove host's ip of  when dhcp release msg is received
-                    hostStore.removeIp(hostId, ip);
+                    providerService.removeIpFromHost(hostId, ip);
                 } else {
                     log.debug("ipAddress not found. Do not add Host for directly connected.");
                 }
@@ -690,19 +703,30 @@
                 ip = extractIpAddress(embeddedDhcp6);
                 if (ip != null) {
                     Set<IpAddress> ips = Sets.newHashSet(ip);
+
+                    // FIXME: we should use vlan id from original packet (solicit, request)
                     VlanId vlanId = clientInterfaces.iterator().next().vlan();
                     HostId hostId = HostId.hostId(clientMac, vlanId);
+                    Host host = hostService.getHost(hostId);
                     HostLocation hostLocation = new HostLocation(clientInterfaces.iterator().next().connectPoint(),
-                            System.currentTimeMillis());
+                                                                 System.currentTimeMillis());
+                    Set<HostLocation> hostLocations = Sets.newHashSet(hostLocation);
+
+                    if (host != null) {
+                        // Dual homing support:
+                        // if host exists, use old locations and new location
+                        hostLocations.addAll(host.locations());
+                    }
                     HostDescription desc = new DefaultHostDescription(clientMac, vlanId,
-                            hostLocation, ips);
+                                                                      hostLocations, ips,
+                                                                      false);
                     log.debug("adding Host for directly connected.");
                     log.debug("client mac {} client vlan {} hostlocation {}",
                             HexString.toHexString(clientMac.toBytes(), ":"),
                             vlanId, hostLocation.toString());
 
                     // Replace the ip when dhcp server give the host new ip address
-                    hostStore.createOrUpdateHost(DhcpRelayManager.PROVIDER_ID, hostId, desc, true);
+                    providerService.hostDetected(hostId, desc, false);
                 } else {
                     log.warn("ipAddress not found. Do not add Host for directly connected.");
                 }
@@ -935,7 +959,8 @@
 
         // find destMac
         MacAddress clientMac = null;
-        Set<Host> clients = hostService.getHostsByIp(Ip6Address.valueOf(dhcp6Relay.getPeerAddress()));
+        Ip6Address peerAddress = Ip6Address.valueOf(dhcp6Relay.getPeerAddress());
+        Set<Host> clients = hostService.getHostsByIp(peerAddress);
         if (clients.isEmpty()) {
             log.warn("There's no host found for this address {}",
                     HexString.toHexString(dhcp6Relay.getPeerAddress(), ":"));
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/DhcpRelayManager.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/DhcpRelayManager.java
index e66dbb6..97b19e7 100644
--- a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/DhcpRelayManager.java
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/DhcpRelayManager.java
@@ -115,8 +115,6 @@
 public class DhcpRelayManager implements DhcpRelayService {
     public static final String DHCP_RELAY_APP = "org.onosproject.dhcprelay";
     public static final ProviderId PROVIDER_ID = new ProviderId("host", DHCP_RELAY_APP);
-    public static final String HOST_LOCATION_PROVIDER =
-            "org.onosproject.provider.host.impl.HostLocationProvider";
     public static final String ROUTE_STORE_IMPL =
             "org.onosproject.routeservice.store.RouteStoreImpl";
     private static final TrafficSelector DHCP_SERVER_SELECTOR = DefaultTrafficSelector.builder()
@@ -243,9 +241,6 @@
         requestDhcpPackets();
         modified(context);
 
-        // disable dhcp from host location provider
-        compCfgService.preSetProperty(HOST_LOCATION_PROVIDER,
-                                      "useDhcp", Boolean.FALSE.toString());
         // Enable distribute route store
         compCfgService.preSetProperty(ROUTE_STORE_IMPL,
                                       "distributed", Boolean.TRUE.toString());
diff --git a/apps/dhcprelay/src/test/java/org/onosproject/dhcprelay/DhcpRelayManagerTest.java b/apps/dhcprelay/src/test/java/org/onosproject/dhcprelay/DhcpRelayManagerTest.java
index a179e63..f45555d 100644
--- a/apps/dhcprelay/src/test/java/org/onosproject/dhcprelay/DhcpRelayManagerTest.java
+++ b/apps/dhcprelay/src/test/java/org/onosproject/dhcprelay/DhcpRelayManagerTest.java
@@ -76,6 +76,7 @@
 import org.onosproject.net.flowobjective.FlowObjectiveService;
 import org.onosproject.net.flowobjective.ForwardingObjective;
 import org.onosproject.net.flowobjective.Objective;
+import org.onosproject.net.host.HostProviderService;
 import org.onosproject.net.intf.Interface;
 import org.onosproject.net.intf.InterfaceServiceAdapter;
 import org.onosproject.net.packet.PacketPriority;
@@ -87,9 +88,7 @@
 import org.onosproject.net.HostLocation;
 import org.onosproject.net.config.NetworkConfigRegistry;
 import org.onosproject.net.host.HostDescription;
-import org.onosproject.net.host.HostEvent;
 import org.onosproject.net.host.HostService;
-import org.onosproject.net.host.HostStoreAdapter;
 import org.onosproject.net.host.InterfaceIpAddress;
 import org.onosproject.net.packet.DefaultInboundPacket;
 import org.onosproject.net.packet.InboundPacket;
@@ -98,7 +97,6 @@
 import org.onosproject.net.packet.PacketContextAdapter;
 import org.onosproject.net.packet.PacketProcessor;
 import org.onosproject.net.packet.PacketServiceAdapter;
-import org.onosproject.net.provider.ProviderId;
 import org.onosproject.store.StoreDelegate;
 import org.osgi.service.component.ComponentContext;
 import org.onlab.packet.DHCP6;
@@ -138,12 +136,28 @@
     private static final VlanId CLIENT_VLAN = VlanId.vlanId("100");
     private static final ConnectPoint CLIENT_CP = ConnectPoint.deviceConnectPoint("of:0000000000000001/1");
     private static final MacAddress CLIENT_IFACE_MAC = MacAddress.valueOf("00:00:00:00:11:01");
+    private static final HostLocation CLIENT_LOCATION = new HostLocation(CLIENT_CP, 0);
+    private static final HostId CLIENT_HOST_ID = HostId.hostId(CLIENT_MAC, CLIENT_VLAN);
+    private static final Ip6Address CLIENT_LL_IP_V6 = Ip6Address.valueOf("fe80::200:00ff:fe00:0001");
+    private static final Host EXISTS_HOST = new DefaultHost(DhcpRelayManager.PROVIDER_ID,
+                                                            CLIENT_HOST_ID, CLIENT_MAC, CLIENT_VLAN,
+                                                            CLIENT_LOCATION, ImmutableSet.of(CLIENT_LL_IP_V6));
     private static final Interface CLIENT_INTERFACE = new Interface("C1",
                                                                     CLIENT_CP,
                                                                     INTERFACE_IPS,
                                                                     CLIENT_IFACE_MAC,
                                                                     CLIENT_VLAN);
 
+    // Dual homing test
+    private static final ConnectPoint CLIENT_DH_CP = ConnectPoint.deviceConnectPoint("of:0000000000000001/3");
+    private static final HostLocation CLIENT_DH_LOCATION = new HostLocation(CLIENT_DH_CP, 0);
+    private static final Interface CLIENT_DH_INTERFACE = new Interface("C1-DH",
+                                                                       CLIENT_DH_CP,
+                                                                       INTERFACE_IPS,
+                                                                       CLIENT_IFACE_MAC,
+                                                                       CLIENT_VLAN);
+
+
     // DHCP client 2 (will send with option 82, so the vlan should equals to vlan from server)
     private static final MacAddress CLIENT2_MAC = MacAddress.valueOf("00:00:00:00:00:01");
     private static final VlanId CLIENT2_VLAN = VlanId.NONE;
@@ -208,7 +222,8 @@
     private static final Set<Interface> INTERFACES = ImmutableSet.of(
             CLIENT_INTERFACE,
             CLIENT2_INTERFACE,
-            SERVER_INTERFACE
+            SERVER_INTERFACE,
+            CLIENT_DH_INTERFACE
     );
     private static final String NON_ONOS_CID = "Non-ONOS circuit ID";
     private static final VlanId IGNORED_VLAN = VlanId.vlanId("100");
@@ -216,9 +231,9 @@
 
     private DhcpRelayManager manager;
     private MockPacketService packetService;
-    private MockHostStore mockHostStore;
     private MockRouteStore mockRouteStore;
     private MockDhcpRelayStore mockDhcpRelayStore;
+    private HostProviderService mockHostProviderService;
 
     @Before
     public void setup() {
@@ -254,20 +269,17 @@
         expect(manager.deviceService.getDevice(DEV_2_ID)).andReturn(device).anyTimes();
         replay(manager.deviceService, device);
 
-        mockHostStore = new MockHostStore();
         mockRouteStore = new MockRouteStore();
         mockDhcpRelayStore = new MockDhcpRelayStore();
         manager.dhcpRelayStore = mockDhcpRelayStore;
 
         manager.interfaceService = new MockInterfaceService();
         manager.flowObjectiveService = EasyMock.niceMock(FlowObjectiveService.class);
-
-
-
+        mockHostProviderService = createNiceMock(HostProviderService.class);
         Dhcp4HandlerImpl v4Handler = new Dhcp4HandlerImpl();
+        v4Handler.providerService = mockHostProviderService;
         v4Handler.dhcpRelayStore = mockDhcpRelayStore;
         v4Handler.hostService = manager.hostService;
-        v4Handler.hostStore = mockHostStore;
         v4Handler.interfaceService = manager.interfaceService;
         v4Handler.packetService = manager.packetService;
         v4Handler.routeStore = mockRouteStore;
@@ -278,10 +290,10 @@
         Dhcp6HandlerImpl v6Handler = new Dhcp6HandlerImpl();
         v6Handler.dhcpRelayStore = mockDhcpRelayStore;
         v6Handler.hostService = manager.hostService;
-        v6Handler.hostStore = mockHostStore;
         v6Handler.interfaceService = manager.interfaceService;
         v6Handler.packetService = manager.packetService;
         v6Handler.routeStore = mockRouteStore;
+        v6Handler.providerService = mockHostProviderService;
         manager.v6Handler = v6Handler;
 
         // properties
@@ -306,33 +318,36 @@
      */
     @Test
     public void relayDhcpWithoutAgentInfo() {
+        replay(mockHostProviderService);
         // send request
         packetService.processPacket(new TestDhcpRequestPacketContext(CLIENT_MAC,
                                                                      CLIENT_VLAN,
                                                                      CLIENT_CP,
                                                                      INTERFACE_IP.ipAddress().getIp4Address(),
                                                                      false));
+        // won't trigger the host provider service
+        verify(mockHostProviderService);
+        reset(mockHostProviderService);
 
-        Set<Host> hosts = ImmutableSet.copyOf(mockHostStore.getHosts());
-        assertEquals(0, hosts.size());
         assertEquals(0, mockRouteStore.routes.size());
 
+        HostId expectHostId = HostId.hostId(CLIENT_MAC, CLIENT_VLAN);
+        Capture<HostDescription> capturedHostDesc = newCapture();
+        mockHostProviderService.hostDetected(eq(expectHostId), capture(capturedHostDesc), eq(false));
+        replay(mockHostProviderService);
         // send ack
         packetService.processPacket(new TestDhcpAckPacketContext(CLIENT_CP, CLIENT_MAC,
                                                                  CLIENT_VLAN, INTERFACE_IP.ipAddress().getIp4Address(),
                                                                  false));
-        hosts = ImmutableSet.copyOf(mockHostStore.getHosts());
-        assertEquals(1, hosts.size());
+        verify(mockHostProviderService);
         assertEquals(0, mockRouteStore.routes.size());
 
-        Host host = hosts.iterator().next();
-        assertEquals(CLIENT_MAC, host.mac());
-        assertEquals(CLIENT_VLAN, host.vlan());
+        HostDescription host = capturedHostDesc.getValue();
+        assertEquals(false, host.configured());
         assertEquals(CLIENT_CP.deviceId(), host.location().elementId());
         assertEquals(CLIENT_CP.port(), host.location().port());
-        assertEquals(1, host.ipAddresses().size());
-        assertEquals(IP_FOR_CLIENT, host.ipAddresses().iterator().next());
-        assertEquals(HostId.hostId(CLIENT_MAC, CLIENT_VLAN), host.id());
+        assertEquals(1, host.ipAddress().size());
+        assertEquals(IP_FOR_CLIENT, host.ipAddress().iterator().next());
     }
 
     /**
@@ -340,6 +355,7 @@
      */
     @Test
     public void relayDhcpWithAgentInfo() {
+        replay(mockHostProviderService);
         // Assume outer dhcp relay agent exists in store already
         // send request
         packetService.processPacket(new TestDhcpRequestPacketContext(CLIENT2_MAC,
@@ -347,11 +363,8 @@
                                                                      CLIENT2_CP,
                                                                      INTERFACE_IP.ipAddress().getIp4Address(),
                                                                      true));
-
-        Set<Host> hosts = ImmutableSet.copyOf(mockHostStore.getHosts());
-        assertEquals(0, hosts.size());
+        // No routes
         assertEquals(0, mockRouteStore.routes.size());
-
         // send ack
         packetService.processPacket(new TestDhcpAckPacketContext(CLIENT2_CP,
                                                                  CLIENT2_MAC,
@@ -359,8 +372,10 @@
                                                                  INTERFACE_IP.ipAddress().getIp4Address(),
                                                                  true));
 
-        hosts = ImmutableSet.copyOf(mockHostStore.getHosts());
-        assertEquals(0, hosts.size());
+        // won't trigger the host provider service
+        verify(mockHostProviderService);
+        reset(mockHostProviderService);
+
         assertEquals(1, mockRouteStore.routes.size());
 
         Route route = mockRouteStore.routes.get(0);
@@ -562,6 +577,7 @@
      */
     @Test
     public void relayDhcp6WithoutAgentInfo() {
+        replay(mockHostProviderService);
         // send request
         packetService.processPacket(new TestDhcp6RequestPacketContext(CLIENT_MAC,
                                                                      VlanId.NONE,
@@ -569,27 +585,28 @@
                                                                      INTERFACE_IP_V6.ipAddress().getIp6Address(),
                                                                      0));
 
-        Set<Host> hosts = ImmutableSet.copyOf(mockHostStore.getHosts());
-        assertEquals(0, hosts.size());
+        verify(mockHostProviderService);
+        reset(mockHostProviderService);
         assertEquals(0, mockRouteStore.routes.size());
 
+        Capture<HostDescription> capturedHostDesc = newCapture();
+        mockHostProviderService.hostDetected(eq(HostId.hostId(CLIENT_MAC, CLIENT_VLAN)),
+                                             capture(capturedHostDesc), eq(false));
+        replay(mockHostProviderService);
         // send reply
         packetService.processPacket(new TestDhcp6ReplyPacketContext(CLIENT_CP, CLIENT_MAC,
                                                                     CLIENT_VLAN,
                                                                     INTERFACE_IP_V6.ipAddress().getIp6Address(),
                                                                     0));
-        hosts = ImmutableSet.copyOf(mockHostStore.getHosts());
-        assertEquals(1, hosts.size());
+        verify(mockHostProviderService);
         assertEquals(0, mockRouteStore.routes.size());
 
-        Host host = hosts.iterator().next();
-        assertEquals(CLIENT_MAC, host.mac());
+        HostDescription host = capturedHostDesc.getValue();
         assertEquals(CLIENT_VLAN, host.vlan());
         assertEquals(CLIENT_CP.deviceId(), host.location().elementId());
         assertEquals(CLIENT_CP.port(), host.location().port());
-        assertEquals(1, host.ipAddresses().size());
-        assertEquals(IP_FOR_CLIENT_V6, host.ipAddresses().iterator().next());
-        assertEquals(HostId.hostId(CLIENT_MAC, CLIENT_VLAN), host.id());
+        assertEquals(1, host.ipAddress().size());
+        assertEquals(IP_FOR_CLIENT_V6, host.ipAddress().iterator().next());
     }
 
     /**
@@ -597,6 +614,7 @@
      */
     @Test
     public void relayDhcp6WithAgentInfo() {
+        replay(mockHostProviderService);
         // Assume outer dhcp6 relay agent exists in store already
         // send request
         packetService.processPacket(new TestDhcp6RequestPacketContext(CLIENT2_MAC,
@@ -605,8 +623,6 @@
                 INTERFACE_IP_V6.ipAddress().getIp6Address(),
                 1));
 
-        Set<Host> hosts = ImmutableSet.copyOf(mockHostStore.getHosts());
-        assertEquals(0, hosts.size());
         assertEquals(0, mockRouteStore.routes.size());
 
         // send reply
@@ -616,8 +632,9 @@
                 INTERFACE_IP_V6.ipAddress().getIp6Address(),
                 1));
 
-        hosts = ImmutableSet.copyOf(mockHostStore.getHosts());
-        assertEquals(0, hosts.size());
+        // won't trigger the host provider service
+        verify(mockHostProviderService);
+        reset(mockHostProviderService);
         assertEquals(2, mockRouteStore.routes.size()); // ipAddress and prefix
 
         Route route = mockRouteStore.routes.get(0);
@@ -625,6 +642,64 @@
         assertEquals(IP_FOR_CLIENT_V6.toIpPrefix(), route.prefix());
         assertEquals(Route.Source.STATIC, route.source());
     }
+
+    @Test
+    public void testDhcp4DualHome() {
+        PacketContext packetContext =
+                new TestDhcpAckPacketContext(CLIENT_DH_CP, CLIENT_MAC, CLIENT_VLAN,
+                                             INTERFACE_IP.ipAddress().getIp4Address(),
+                                             false);
+        reset(manager.hostService);
+        expect(manager.hostService.getHost(CLIENT_HOST_ID)).andReturn(EXISTS_HOST).anyTimes();
+        Capture<HostDescription> capturedHostDesc = newCapture();
+        mockHostProviderService.hostDetected(eq(CLIENT_HOST_ID), capture(capturedHostDesc), eq(false));
+        replay(mockHostProviderService, manager.hostService);
+        packetService.processPacket(packetContext);
+        verify(mockHostProviderService);
+
+        HostDescription hostDesc = capturedHostDesc.getValue();
+        Set<HostLocation> hostLocations = hostDesc.locations();
+        assertEquals(2, hostLocations.size());
+        assertTrue(hostLocations.contains(CLIENT_LOCATION));
+        assertTrue(hostLocations.contains(CLIENT_DH_LOCATION));
+    }
+
+    @Test
+    public void testDhcp6DualHome() {
+        PacketContext packetContext =
+                new TestDhcp6ReplyPacketContext(CLIENT_DH_CP, CLIENT_MAC, CLIENT_VLAN,
+                                                INTERFACE_IP_V6.ipAddress().getIp6Address(),
+                                                0);
+        reset(manager.hostService);
+        expect(manager.hostService.getHostsByIp(CLIENT_LL_IP_V6)).andReturn(ImmutableSet.of(EXISTS_HOST)).anyTimes();
+
+        // FIXME: currently DHCPv6 has a bug, we can't get correct vlan of client......
+        // XXX: The vlan relied from DHCP6 handler might be wrong, do hack here
+        HostId hostId = HostId.hostId(CLIENT_MAC, VlanId.NONE);
+        expect(manager.hostService.getHost(hostId)).andReturn(EXISTS_HOST).anyTimes();
+
+        // XXX: sometimes this will work, sometimes not
+         expect(manager.hostService.getHost(CLIENT_HOST_ID)).andReturn(EXISTS_HOST).anyTimes();
+
+        Capture<HostDescription> capturedHostDesc = newCapture();
+
+        // XXX: also a hack here
+        mockHostProviderService.hostDetected(eq(hostId), capture(capturedHostDesc), eq(false));
+        expectLastCall().anyTimes();
+
+        mockHostProviderService.hostDetected(eq(CLIENT_HOST_ID), capture(capturedHostDesc), eq(false));
+        expectLastCall().anyTimes();
+        replay(mockHostProviderService, manager.hostService);
+        packetService.processPacket(packetContext);
+        verify(mockHostProviderService);
+
+        HostDescription hostDesc = capturedHostDesc.getValue();
+        Set<HostLocation> hostLocations = hostDesc.locations();
+        assertEquals(2, hostLocations.size());
+        assertTrue(hostLocations.contains(CLIENT_LOCATION));
+        assertTrue(hostLocations.contains(CLIENT_DH_LOCATION));
+    }
+
     private static class MockDefaultDhcpRelayConfig extends DefaultDhcpRelayConfig {
         @Override
         public boolean isValid() {
@@ -692,36 +767,6 @@
         }
     }
 
-    private class MockHostStore extends HostStoreAdapter {
-
-        private final Map<HostId, HostDescription> hosts = Maps.newHashMap();
-
-        @Override
-        public HostEvent createOrUpdateHost(ProviderId providerId, HostId hostId,
-                                            HostDescription hostDescription,
-                                            boolean replaceIps) {
-            hosts.put(hostId, hostDescription);
-
-            // not necessary to return host event in this test.
-            return null;
-        }
-
-        public HostDescription hostDesc(HostId hostId) {
-            return hosts.get(hostId);
-        }
-
-        @Override
-        public Iterable<Host> getHosts() {
-            return hosts.values().stream()
-                    .map(hd -> new DefaultHost(DhcpRelayManager.PROVIDER_ID,
-                                               HostId.hostId(hd.hwAddress(), hd.vlan()),
-                                               hd.hwAddress(),
-                                               hd.vlan(), hd.locations(),
-                                               hd.ipAddress(), false))
-                    .collect(Collectors.toList());
-        }
-    }
-
     private class MockRouteStore extends RouteStoreAdapter {
         private List<Route> routes = Lists.newArrayList();
 
@@ -1231,10 +1276,10 @@
             byte[] interfaceId = buildInterfaceId(clientMac, clientCp);
             DHCP6 dhcp6 = new DHCP6();
             buildRelayMsg(dhcp6, DHCP6.MsgType.RELAY_REPL.value(),
-                    INTERFACE_IP_V6.ipAddress().getIp6Address(),
-                    OUTER_RELAY_IP_V6,
-                    (byte) 0, interfaceId,
-                    dhcp6Payload);
+                          INTERFACE_IP_V6.ipAddress().getIp6Address(),
+                          CLIENT_LL_IP_V6,
+                          (byte) 0, interfaceId,
+                          dhcp6Payload);
 
             DHCP6 dhcp6Parent = null;
             DHCP6 dhcp6Child = dhcp6;
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 90efcf4..a0d6e37 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
@@ -151,13 +151,11 @@
     private boolean requestIpv6ND = false;
 
     @Property(name = "useDhcp", boolValue = false,
-            label = "Use DHCP for neighbor discovery by the " +
-                    "Host Location Provider; default is false")
+            label = "Use DHCP to update IP address of the host; default is false")
     private boolean useDhcp = false;
 
     @Property(name = "useDhcp6", boolValue = false,
-            label = "Use DHCPv6 for neighbor discovery by the " +
-                    "Host Location Provider; default is false")
+            label = "Use DHCPv6 to update IP address of the host; default is false")
     private boolean useDhcp6 = false;
 
     @Property(name = "requestInterceptsEnabled", boolValue = true,
@@ -541,25 +539,18 @@
 
             // IPv4: update location only
             } else if (eth.getEtherType() == Ethernet.TYPE_IPV4) {
-                DHCP dhcp = findDhcp(eth).orElse(null);
-                if (dhcp != null) {
-                    if (useDhcp) {
-                        // learn host (server or client) MAC address
-                        createOrUpdateHost(hid, srcMac, vlan, hloc, null);
-
-                        // DHCP ACK: additionally update IP of DHCP client
-                        if (dhcp.getPacketType().equals(DHCP.MsgType.DHCPACK)) {
-                            MacAddress hostMac = MacAddress.valueOf(dhcp.getClientHardwareAddress());
-                            VlanId hostVlan = VlanId.vlanId(eth.getVlanID());
-                            HostId hostId = HostId.hostId(hostMac, hostVlan);
-                            updateHostIp(hostId, IpAddress.valueOf(dhcp.getYourIPAddress()));
-                        }
+                // Update host location
+                createOrUpdateHost(hid, srcMac, vlan, hloc, null);
+                if (useDhcp) {
+                    DHCP dhcp = findDhcp(eth).orElse(null);
+                    // DHCP ACK: additionally update IP of DHCP client
+                    if (dhcp != null  && dhcp.getPacketType().equals(DHCP.MsgType.DHCPACK)) {
+                        MacAddress hostMac = MacAddress.valueOf(dhcp.getClientHardwareAddress());
+                        VlanId hostVlan = VlanId.vlanId(eth.getVlanID());
+                        HostId hostId = HostId.hostId(hostMac, hostVlan);
+                        updateHostIp(hostId, IpAddress.valueOf(dhcp.getYourIPAddress()));
                     }
-                } else {
-                    // learn host MAC address
-                    createOrUpdateHost(hid, srcMac, vlan, hloc, null);
                 }
-            //
             // NeighborAdvertisement and NeighborSolicitation: possible
             // new hosts, update both location and IP.
             //
@@ -605,8 +596,8 @@
                     }
                 }
 
-                // multicast
-                if (eth.isMulticast()) {
+                // multicast, exclude DHCPv6
+                if (eth.isMulticast() && dhcp6 == null) {
                     return;
                 }
 
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 5c924b5..77e2caa 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
@@ -439,18 +439,6 @@
     }
 
     /**
-     * The host store should not updated when we disabled "useDhcp".
-     */
-    @Test
-    public void receiveDhcpButNotEnabled() {
-        TestUtils.setField(provider, "useDhcp", false);
-        // DHCP Request
-        testProcessor.process(new TestDhcpRequestPacketContext(DEV1, VLAN));
-        assertThat("receiveDhcpButNotEnabled. No host description expected",
-                   providerService.descriptions.size(), is(0));
-    }
-
-    /**
      * When receiving NeighborAdvertisement, updates location and IP.
      */
     @Test