[ONOS-7983] Use IPv6 RA and RS to discover hosts

Change-Id: I5ee3dcb93536ab8f0cc760b38bf0bf73d63e3547
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 2e4b412..874bf38 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
@@ -115,6 +115,7 @@
                 HOST_REMOVAL_ENABLED + ":Boolean=" + HOST_REMOVAL_ENABLED_DEFAULT,
                 REQUEST_ARP + ":Boolean=" + REQUEST_ARP_DEFAULT,
                 REQUEST_NDP + ":Boolean=" + REQUEST_NDP_DEFAULT,
+                REQUEST_NDP_RS_RA + ":Boolean=" + REQUEST_NDP_RS_RA_DEFAULT,
                 USE_DHCP + ":Boolean=" + USE_DHCP_DEFAULT,
                 USE_DHCP6 + ":Boolean=" + USE_DHCP6_DEFAULT,
                 REQUEST_INTERCEPTS_ENABLED + ":Boolean=" + REQUEST_INTERCEPTS_ENABLED_DEFAULT,
@@ -162,9 +163,12 @@
     /** Request ARP packets for neighbor discovery by the Host Location Provider; default is true. */
     private boolean requestArp = true;
 
-    /** Requests IPv6 Neighbor Discovery by the Host Location Provider; default is false. */
+    /** Requests IPv6 NDP Neighbor Solicitation and Advertisement by the Host Location Provider; default is false. */
     private boolean requestIpv6ND = false;
 
+    /** Requests IPv6 NDP Router Solicitation and Advertisement by the Host Location Provider; default is false. */
+    private boolean requestIpv6NdpRsRa = false;
+
     /** Use DHCP to update IP address of the host; default is false. */
     private boolean useDhcp = false;
 
@@ -257,32 +261,40 @@
      */
     private void requestIntercepts() {
         // Use ARP
-        TrafficSelector arpSelector = DefaultTrafficSelector.builder()
-                .matchEthType(Ethernet.TYPE_ARP)
-                .build();
+        TrafficSelector.Builder selector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_ARP);
         if (requestArp) {
-            packetService.requestPackets(arpSelector, PacketPriority.CONTROL, appId);
+            packetService.requestPackets(selector.build(), PacketPriority.CONTROL, appId);
         } else {
-            packetService.cancelPackets(arpSelector, PacketPriority.CONTROL, appId);
+            packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId);
         }
 
-        // Use IPv6 Neighbor Discovery
-        TrafficSelector ipv6NsSelector = DefaultTrafficSelector.builder()
-                .matchEthType(Ethernet.TYPE_IPV6)
-                .matchIPProtocol(IPv6.PROTOCOL_ICMP6)
-                .matchIcmpv6Type(ICMP6.NEIGHBOR_SOLICITATION)
-                .build();
-        TrafficSelector ipv6NaSelector = DefaultTrafficSelector.builder()
-                .matchEthType(Ethernet.TYPE_IPV6)
-                .matchIPProtocol(IPv6.PROTOCOL_ICMP6)
-                .matchIcmpv6Type(ICMP6.NEIGHBOR_ADVERTISEMENT)
-                .build();
+        // Use IPv6 NDP Neighbor Solicitation and Advertisement
+        selector.matchEthType(Ethernet.TYPE_IPV6)
+                .matchIPProtocol(IPv6.PROTOCOL_ICMP6);
         if (requestIpv6ND) {
-            packetService.requestPackets(ipv6NsSelector, PacketPriority.CONTROL, appId);
-            packetService.requestPackets(ipv6NaSelector, PacketPriority.CONTROL, appId);
+            selector.matchIcmpv6Type(ICMP6.NEIGHBOR_SOLICITATION);
+            packetService.requestPackets(selector.build(), PacketPriority.CONTROL, appId);
+            selector.matchIcmpv6Type(ICMP6.NEIGHBOR_ADVERTISEMENT);
+            packetService.requestPackets(selector.build(), PacketPriority.CONTROL, appId);
         } else {
-            packetService.cancelPackets(ipv6NsSelector, PacketPriority.CONTROL, appId);
-            packetService.cancelPackets(ipv6NaSelector, PacketPriority.CONTROL, appId);
+            selector.matchIcmpv6Type(ICMP6.NEIGHBOR_SOLICITATION);
+            packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId);
+            selector.matchIcmpv6Type(ICMP6.NEIGHBOR_ADVERTISEMENT);
+            packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId);
+        }
+
+        // Use IPv6 NDP Router Solicitation and Advertisement
+        if (requestIpv6NdpRsRa) {
+            selector.matchIcmpv6Type(ICMP6.ROUTER_SOLICITATION);
+            packetService.requestPackets(selector.build(), PacketPriority.CONTROL, appId);
+            selector.matchIcmpv6Type(ICMP6.ROUTER_ADVERTISEMENT);
+            packetService.requestPackets(selector.build(), PacketPriority.CONTROL, appId);
+        } else {
+            selector.matchIcmpv6Type(ICMP6.ROUTER_SOLICITATION);
+            packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId);
+            selector.matchIcmpv6Type(ICMP6.ROUTER_ADVERTISEMENT);
+            packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId);
         }
     }
 
@@ -340,10 +352,20 @@
                              "using current value of {}", requestIpv6ND);
         } else {
             requestIpv6ND = flag;
-            log.info("Configured. Using IPv6 Neighbor Discovery is {}",
+            log.info("Configured. Using IPv6 NDP Neighbor Solicitation and Advertisement is {}",
                      requestIpv6ND ? "enabled" : "disabled");
         }
 
+        flag = Tools.isPropertyEnabled(properties, REQUEST_NDP_RS_RA);
+        if (flag == null) {
+            log.info("Using IPv6 Neighbor Discovery is not configured, " +
+                    "using current value of {}", requestIpv6NdpRsRa);
+        } else {
+            requestIpv6NdpRsRa = flag;
+            log.info("Configured. Using IPv6 NDP Router Solicitation and Advertisement is {}",
+                    requestIpv6NdpRsRa ? "enabled" : "disabled");
+        }
+
         flag = Tools.isPropertyEnabled(properties, USE_DHCP);
         if (flag == null) {
             log.info("Using DHCP is not configured, " +
@@ -610,8 +632,12 @@
                     // Neighbor Discovery Protocol
                     pkt = pkt.getPayload();
                     if (pkt != null) {
-                        // RouterSolicitation, RouterAdvertisement
-                        if (pkt instanceof RouterAdvertisement || pkt instanceof RouterSolicitation) {
+                        if ((pkt instanceof RouterAdvertisement || pkt instanceof RouterSolicitation)) {
+                            if (ip.isZero()) {
+                                return;
+                            }
+                            // RouterSolicitation, RouterAdvertisement
+                            createOrUpdateHost(hid, srcMac, vlan, innerVlan, outerTpid, hloc, ip);
                             return;
                         }
                         if (pkt instanceof NeighborSolicitation || pkt instanceof NeighborAdvertisement) {
diff --git a/providers/host/src/main/java/org/onosproject/provider/host/impl/OsgiPropertyConstants.java b/providers/host/src/main/java/org/onosproject/provider/host/impl/OsgiPropertyConstants.java
index eb8dafe..ac7035b 100644
--- a/providers/host/src/main/java/org/onosproject/provider/host/impl/OsgiPropertyConstants.java
+++ b/providers/host/src/main/java/org/onosproject/provider/host/impl/OsgiPropertyConstants.java
@@ -34,6 +34,9 @@
     public static final String REQUEST_NDP = "requestIpv6ND";
     public static final boolean REQUEST_NDP_DEFAULT = false;
 
+    public static final String REQUEST_NDP_RS_RA = "requestIpv6NdpRsRa";
+    public static final boolean REQUEST_NDP_RS_RA_DEFAULT = false;
+
     public static final String USE_DHCP = "useDhcp";
     public static final boolean USE_DHCP_DEFAULT = false;
 
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 9971921..0fab86e 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
@@ -506,23 +506,35 @@
     }
 
     /**
-     * When receiving RouterAdvertisement, ignores it.
+     * When receiving RouterAdvertisement, update location and IP.
      */
     @Test
     public void receivesRa() {
         testProcessor.process(new TestRAPacketContext(DEV4));
         TestTools.assertAfter(ASSERTION_DELAY, () -> assertThat("receivesRa. No host description expected",
-                providerService.descriptions.size(), is(0)));
+                providerService.descriptions.size(), is(1)));
+
+        final HostDescription desc = providerService.descriptions.get(0);
+        TestTools.assertAfter(ASSERTION_DELAY, () -> assertThat(desc.location(), is(LOCATION2)));
+        TestTools.assertAfter(ASSERTION_DELAY, () -> assertThat(desc.hwAddress(), is(MAC2)));
+        TestTools.assertAfter(ASSERTION_DELAY, () -> assertThat(desc.ipAddress().toArray()[0], is(IP_ADDRESS2)));
+        TestTools.assertAfter(ASSERTION_DELAY, () -> assertThat(desc.vlan(), is(VLAN)));
     }
 
     /**
-     * When receiving RouterSolicitation, ignores it.
+     * When receiving RouterSolicitation, update location and IP.
      */
     @Test
     public void receiveRs() {
         testProcessor.process(new TestRSPacketContext(DEV4));
         TestTools.assertAfter(ASSERTION_DELAY, () -> assertThat("receiveRs. No host description expected",
-                providerService.descriptions.size(), is(0)));
+                providerService.descriptions.size(), is(1)));
+
+        final HostDescription desc = providerService.descriptions.get(0);
+        TestTools.assertAfter(ASSERTION_DELAY, () -> assertThat(desc.location(), is(LOCATION2)));
+        TestTools.assertAfter(ASSERTION_DELAY, () -> assertThat(desc.hwAddress(), is(MAC2)));
+        TestTools.assertAfter(ASSERTION_DELAY, () -> assertThat(desc.ipAddress().toArray()[0], is(IP_ADDRESS2)));
+        TestTools.assertAfter(ASSERTION_DELAY, () -> assertThat(desc.vlan(), is(VLAN)));
     }
 
     /**
@@ -1034,7 +1046,7 @@
             IPv6 ipv6 = new IPv6();
             ipv6.setPayload(icmp6);
             ipv6.setDestinationAddress(Ip6Address.valueOf("ff02::2").toOctets());
-            ipv6.setSourceAddress(Ip6Address.valueOf("::").toOctets());
+            ipv6.setSourceAddress(IP2);
             Ethernet eth = new Ethernet();
             eth.setEtherType(Ethernet.TYPE_IPV6)
                     .setVlanID(VLAN.toShort())