IPv6 Unsolicited RA application.

Change-Id: I7dac31918708d85247a7831fd38f83d2e8781183
diff --git a/apps/pom.xml b/apps/pom.xml
index a5dba52..8ed0f84 100644
--- a/apps/pom.xml
+++ b/apps/pom.xml
@@ -91,13 +91,14 @@
         <module>yang</module>
         <module>openroadm</module>
         <module>netconf/client</module>
-	    <module>gluon</module>
+        <module>gluon</module>
         <module>evpnopenflow</module>
         <module>route-service</module>
         <module>evpn-route-service</module>
         <module>l3vpn</module>
         <module>openstacknetworkingui</module>
         <module>cfm</module>
+        <module>routeradvertisement</module>
     </modules>
 
     <properties>
diff --git a/apps/routeradvertisement/BUCK b/apps/routeradvertisement/BUCK
new file mode 100644
index 0000000..9673f0c
--- /dev/null
+++ b/apps/routeradvertisement/BUCK
@@ -0,0 +1,15 @@
+COMPILE_DEPS = [
+    '//lib:CORE_DEPS',
+    '//incubator/api:onos-incubator-api'
+]
+
+osgi_jar (
+    deps = COMPILE_DEPS,
+)
+
+onos_app (
+    title = 'IPv6 RA Generator',
+    category = 'Traffic Steering',
+    url = 'http://onosproject.org',
+    description = 'Application for generating IPv6 RAs',
+)
diff --git a/apps/routeradvertisement/pom.xml b/apps/routeradvertisement/pom.xml
new file mode 100644
index 0000000..ecf6c38
--- /dev/null
+++ b/apps/routeradvertisement/pom.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2015-present Open Networking Foundation
+  ~ Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China
+  ~ Advisers: Keqiu Li and Heng Qi
+  ~ This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002)
+  ~ and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<!--suppress MavenModelInspection -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.onosproject</groupId>
+        <artifactId>onos-apps</artifactId>
+        <version>1.12.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>onos-app-routeradvertisement</artifactId>
+    <version>1.12.0-SNAPSHOT</version>
+    <packaging>bundle</packaging>
+
+    <description>ONOS IPv6 RA application</description>
+    <url>http://onosproject.org</url>
+
+    <properties>
+        <onos.app.name>org.infosys.routeradvertisement</onos.app.name>
+        <onos.app.title>IPv6 RA Generator</onos.app.title>
+        <onos.app.category>Traffic Steering</onos.app.category>
+        <onos.app.url>http://onosproject.org</onos.app.url>
+        <onos.app.readme>Application for generating IPv6 RA</onos.app.readme>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.scr.annotations</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-incubator-api</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onlab-misc</artifactId>
+        </dependency>
+
+    </dependencies>
+
+</project>
diff --git a/apps/routeradvertisement/src/main/java/org/onosproject/ra/RouterAdvertisementManager.java b/apps/routeradvertisement/src/main/java/org/onosproject/ra/RouterAdvertisementManager.java
new file mode 100644
index 0000000..cfefa84
--- /dev/null
+++ b/apps/routeradvertisement/src/main/java/org/onosproject/ra/RouterAdvertisementManager.java
@@ -0,0 +1,345 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.ra;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.packet.EthType;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.ICMP6;
+import org.onlab.packet.IPv6;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.Ip6Address;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.ndp.RouterAdvertisement;
+import org.onlab.packet.ndp.NeighborDiscoveryOptions;
+import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.MastershipRole;
+import org.onosproject.net.intf.Interface;
+import org.onosproject.net.intf.InterfaceEvent;
+import org.onosproject.net.intf.InterfaceListener;
+import org.onosproject.net.intf.InterfaceService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.host.InterfaceIpAddress;
+import org.onosproject.net.packet.DefaultOutboundPacket;
+import org.onosproject.net.packet.OutboundPacket;
+import org.onosproject.net.packet.PacketService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.osgi.service.component.ComponentContext;
+
+import javax.annotation.concurrent.GuardedBy;
+import java.nio.ByteBuffer;
+import java.util.Dictionary;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Arrays;
+import java.util.Optional;
+
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.IntStream;
+
+import static com.google.common.base.Strings.isNullOrEmpty;
+import static org.onlab.util.Tools.get;
+import static org.onlab.util.Tools.groupedThreads;
+
+/**
+ * Manages IPv6 Router Advertisements.
+ */
+@Component(immediate = true)
+public class RouterAdvertisementManager {
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+    private static final String PROP_RA_THREADS_POOL = "raPoolSize";
+    private static final int DEFAULT_RA_THREADS_POOL_SIZE = 10;
+    private static final String PROP_RA_THREADS_DELAY = "raThreadDelay";
+    private static final int DEFAULT_RA_THREADS_DELAY = 5;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    PacketService packetService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ComponentConfigService componentConfigService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    public InterfaceService interfaceService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    public MastershipService mastershipService;
+
+    @Property(name = PROP_RA_THREADS_POOL, intValue = DEFAULT_RA_THREADS_POOL_SIZE,
+            label = "Router Advertisement thread pool capacity")
+    protected int raPoolSize = DEFAULT_RA_THREADS_POOL_SIZE;
+
+    @Property(name = PROP_RA_THREADS_DELAY, intValue = DEFAULT_RA_THREADS_DELAY,
+            label = "Router Advertisement thread delay in seconds")
+    protected int raThreadDelay = DEFAULT_RA_THREADS_DELAY;
+
+    private ScheduledExecutorService executors = null;
+
+    @GuardedBy(value = "this")
+    private final Map<ConnectPoint, ScheduledFuture<?>> transmitters = new LinkedHashMap<>();
+
+    private static final String APP_NAME = "org.onosproject.routeradvertisement";
+    private ApplicationId appId;
+
+    // Listener for handling dynamic interface modifications.
+    private class InternalInterfaceListener implements InterfaceListener {
+        @Override
+        public void event(InterfaceEvent event) {
+            Interface i = event.subject();
+            switch (event.type()) {
+                case INTERFACE_ADDED:
+                    if (mastershipService.getLocalRole(i.connectPoint().deviceId())
+                            == MastershipRole.MASTER) {
+                        activateRouterAdvertisement(i.connectPoint(),
+                                i.ipAddressesList());
+                    }
+                    break;
+                case INTERFACE_REMOVED:
+                    if (mastershipService.getLocalRole(i.connectPoint().deviceId())
+                            == MastershipRole.MASTER) {
+                        deactivateRouterAdvertisement(i.connectPoint(),
+                                i.ipAddressesList());
+                    }
+                    break;
+                case INTERFACE_UPDATED:
+                    break;
+                default:
+                    break;
+            }
+        }
+    }
+    private final InterfaceListener interfaceListener = new InternalInterfaceListener();
+
+    // Enables RA threads on 'connectPoint' with configured IPv6s
+    private void activateRouterAdvertisement(ConnectPoint connectPoint, List<InterfaceIpAddress> addresses) {
+        synchronized (this) {
+            RAWorkerThread worker = new RAWorkerThread(connectPoint, addresses, raThreadDelay);
+            ScheduledFuture<?> handler = executors.scheduleAtFixedRate(worker, raThreadDelay,
+                    raThreadDelay, TimeUnit.SECONDS);
+            transmitters.put(connectPoint, handler);
+        }
+
+    }
+
+    // Disables already activated RA threads on 'connectPoint'
+    private void deactivateRouterAdvertisement(ConnectPoint connectPoint, List<InterfaceIpAddress> addresses) {
+        synchronized (this) {
+            if (connectPoint != null) {
+                ScheduledFuture<?> handler = transmitters.get(connectPoint);
+                handler.cancel(false);
+                transmitters.remove(connectPoint);
+            }
+        }
+    }
+
+    @Activate
+    protected void activate(ComponentContext context) {
+        // Basic application registrations.
+        appId = coreService.registerApplication(APP_NAME);
+        componentConfigService.registerProperties(getClass());
+
+        // Loading configured properties.
+        if (context != null) {
+            Dictionary<?, ?> properties = context.getProperties();
+            try {
+                String s = get(properties, PROP_RA_THREADS_POOL);
+                raPoolSize = isNullOrEmpty(s) ?
+                        DEFAULT_RA_THREADS_POOL_SIZE : Integer.parseInt(s.trim());
+
+                s = get(properties, PROP_RA_THREADS_DELAY);
+                raThreadDelay = isNullOrEmpty(s) ?
+                        DEFAULT_RA_THREADS_DELAY : Integer.parseInt(s.trim());
+
+            } catch (NumberFormatException e) {
+                log.warn("Component configuration had invalid value, loading default values.", e);
+            }
+        }
+
+        // Interface listener for dynamic RA handling.
+        interfaceService.addListener(interfaceListener);
+
+        // Initialize RA thread pool
+        executors = Executors.newScheduledThreadPool(raPoolSize,
+                groupedThreads("RouterAdvertisement", "event-%d", log));
+
+        // Start Router Advertisement Transmission for all configured interfaces.
+        interfaceService.getInterfaces()
+                .stream()
+                .filter(i -> mastershipService.getLocalRole(i.connectPoint().deviceId())
+                        == MastershipRole.MASTER)
+                .filter(i -> i.ipAddressesList()
+                        .stream()
+                        .anyMatch(ia -> ia.ipAddress().version().equals(IpAddress.Version.INET6)))
+                .forEach(j ->
+                        activateRouterAdvertisement(j.connectPoint(), j.ipAddressesList())
+                );
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        // Unregister resources.
+        componentConfigService.unregisterProperties(getClass(), false);
+        interfaceService.removeListener(interfaceListener);
+
+        // Clear out Router Advertisement Transmission for all configured interfaces.
+        interfaceService.getInterfaces()
+                .stream()
+                .filter(i -> mastershipService.getLocalRole(i.connectPoint().deviceId())
+                        == MastershipRole.MASTER)
+                .filter(i -> i.ipAddressesList()
+                        .stream()
+                        .anyMatch(ia -> ia.ipAddress().version().equals(IpAddress.Version.INET6)))
+                .forEach(j ->
+                        deactivateRouterAdvertisement(j.connectPoint(), j.ipAddressesList())
+                );
+    }
+
+    // Worker thread for actually sending ICMPv6 RA packets.
+    private class RAWorkerThread implements Runnable {
+
+        ConnectPoint connectPoint;
+        List<InterfaceIpAddress> ipAddresses;
+        int retransmitPeriod;
+
+        // Various fixed values in RA packet
+        public static final byte RA_HOP_LIMIT = (byte) 0xff;
+        public static final short RA_ROUTER_LIFETIME = (short) 1800;
+        public static final int RA_OPTIONS_BUFFER_SIZE = 500;
+        public static final int RA_OPTION_MTU_VALUE = 1500;
+        public static final int RA_OPTION_PREFIX_VALID_LIFETIME = 600;
+        public static final int RA_OPTION_PREFIX_PREFERRED_LIFETIME = 600;
+        public static final int RA_RETRANSMIT_CALIBRATION_PERIOD = 1;
+
+
+        RAWorkerThread(ConnectPoint connectPoint, List<InterfaceIpAddress> ipAddresses, int period) {
+            this.connectPoint = connectPoint;
+            this.ipAddresses = ipAddresses;
+            retransmitPeriod = period;
+        }
+
+        public void run() {
+            // Router Advertisement header filling. Please refer RFC-2461.
+            RouterAdvertisement ra = new RouterAdvertisement();
+            ra.setCurrentHopLimit(RA_HOP_LIMIT);
+            ra.setMFlag((byte) 0x01);
+            ra.setOFlag((byte) 0x00);
+            ra.setRouterLifetime(RA_ROUTER_LIFETIME);
+            ra.setReachableTime(0);
+            ra.setRetransmitTimer(retransmitPeriod + RA_RETRANSMIT_CALIBRATION_PERIOD);
+
+            // Option : Source link-layer address.
+            byte[] optionBuffer = new byte[RA_OPTIONS_BUFFER_SIZE];
+            ByteBuffer option = ByteBuffer.wrap(optionBuffer);
+            Optional<MacAddress> macAddress = interfaceService.getInterfacesByPort(connectPoint).stream()
+                    .map(Interface::mac).findFirst();
+            if (!macAddress.isPresent()) {
+                log.warn("Unable to retrieve interface {} MAC address. Terminating RA transmission.", connectPoint);
+                return;
+            }
+            option.put(macAddress.get().toBytes());
+            ra.addOption(NeighborDiscoveryOptions.TYPE_SOURCE_LL_ADDRESS,
+                    Arrays.copyOfRange(option.array(), 0, option.position()));
+
+            // Option : MTU.
+            option.rewind();
+            option.putShort((short) 0);
+            option.putInt(RA_OPTION_MTU_VALUE);
+            ra.addOption(NeighborDiscoveryOptions.TYPE_MTU,
+                    Arrays.copyOfRange(option.array(), 0, option.position()));
+
+            // Option : Prefix information.
+            ipAddresses.stream()
+                    .filter(i -> i.ipAddress().version().equals(IpAddress.Version.INET6))
+                    .forEach(i -> {
+                        option.rewind();
+                        option.put((byte) i.subnetAddress().prefixLength());
+                        // Enable "onlink" option only.
+                        option.put((byte) 0x80);
+                        option.putInt(RA_OPTION_PREFIX_VALID_LIFETIME);
+                        option.putInt(RA_OPTION_PREFIX_PREFERRED_LIFETIME);
+                        // Clear reserved fields
+                        option.putInt(0x00000000);
+                        option.put(IpAddress.makeMaskedAddress(i.ipAddress(),
+                                i.subnetAddress().prefixLength()).toOctets());
+                        ra.addOption(NeighborDiscoveryOptions.TYPE_PREFIX_INFORMATION,
+                                Arrays.copyOfRange(option.array(), 0, option.position()));
+
+                    });
+
+            // ICMPv6 header filling.
+            ICMP6 icmpv6 = new ICMP6();
+            icmpv6.setIcmpType(ICMP6.ROUTER_ADVERTISEMENT);
+            icmpv6.setIcmpCode((byte) 0);
+            icmpv6.setPayload(ra);
+
+            // IPv6 header filling.
+            byte[] ip6AllNodesAddress = Ip6Address.valueOf("ff02::1").toOctets();
+            IPv6 ipv6 = new IPv6();
+            ipv6.setDestinationAddress(ip6AllNodesAddress);
+            /* RA packet L2 source address created from port MAC address.
+             * Note : As per RFC-4861 RAs should be sent from link-local address.
+             */
+            ipv6.setSourceAddress(IPv6.getLinkLocalAddress(macAddress.get().toBytes()));
+            ipv6.setNextHeader(IPv6.PROTOCOL_ICMP6);
+            ipv6.setHopLimit(RA_HOP_LIMIT);
+            ipv6.setTrafficClass((byte) 0xe0);
+            ipv6.setPayload(icmpv6);
+
+            // Ethernet header filling.
+            Ethernet ethernet = new Ethernet();
+
+            /* Ethernet IPv6 multicast address creation.
+             * Refer : RFC 2624 section 7.
+             */
+            byte[] l2Ipv6MulticastAddress = MacAddress.IPV6_MULTICAST.toBytes();
+            IntStream.range(1, 4).forEach(i -> l2Ipv6MulticastAddress[l2Ipv6MulticastAddress.length - i] =
+                    ip6AllNodesAddress[ip6AllNodesAddress.length - i]);
+
+            ethernet.setDestinationMACAddress(MacAddress.valueOf(l2Ipv6MulticastAddress));
+            ethernet.setSourceMACAddress(macAddress.get().toBytes());
+            ethernet.setEtherType(EthType.EtherType.IPV6.ethType().toShort());
+            ethernet.setVlanID(Ethernet.VLAN_UNTAGGED);
+            ethernet.setPayload(ipv6);
+            ethernet.setPad(false);
+
+            // Flush out PACKET_OUT.
+            ByteBuffer stream = ByteBuffer.wrap(ethernet.serialize());
+            TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(connectPoint.port()).build();
+            OutboundPacket packet = new DefaultOutboundPacket(connectPoint.deviceId(),
+                    treatment, stream);
+            packetService.emit(packet);
+        }
+    }
+}
diff --git a/apps/routeradvertisement/src/main/java/org/onosproject/ra/package-info.java b/apps/routeradvertisement/src/main/java/org/onosproject/ra/package-info.java
new file mode 100644
index 0000000..c99fd94
--- /dev/null
+++ b/apps/routeradvertisement/src/main/java/org/onosproject/ra/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * IPv6 Router Advertisement application.
+ */
+package org.onosproject.ra;
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java
index 23bbf07..4eec890 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java
@@ -31,6 +31,7 @@
 import org.onosproject.net.neighbour.NeighbourMessageType;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
+import org.onosproject.net.intf.Interface;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.host.HostService;
@@ -204,21 +205,31 @@
         IPv6 ipv6Packet = (IPv6) eth.getPayload();
         Ip6Address destinationAddress = Ip6Address.valueOf(ipv6Packet.getDestinationAddress());
         Set<IpAddress> gatewayIpAddresses = config.getPortIPs(deviceId);
+        MacAddress interfaceMac;
         IpAddress routerIp;
+
         try {
             routerIp = config.getRouterIpv6(deviceId);
+            Optional<MacAddress> macAddress = srManager.interfaceService.getInterfacesByPort(inPort).stream()
+                    .map(Interface::mac)
+                    .findFirst();
+            if (!macAddress.isPresent()) {
+                log.warn("Failed in fetching MAC address of {}. Aborting ICMP6 processing.", inPort);
+                return;
+            }
+            interfaceMac = MacAddress.valueOf(macAddress.get().toBytes());
+
+            // Ensure ICMP to the router IP, gateway IP or link-local EUI-64
+            ICMP6 icmp6 = (ICMP6) ipv6Packet.getPayload();
+            if (icmp6.getIcmpType() == ICMP6.ECHO_REQUEST && (destinationAddress.equals(routerIp.getIp6Address()) ||
+                    destinationAddress.equals(Ip6Address.valueOf(IPv6.getLinkLocalAddress(interfaceMac.toBytes()))) ||
+                    gatewayIpAddresses.contains(destinationAddress))) {
+                sendIcmpv6Response(eth, inPort);
+            } else {
+                log.trace("Ignore ICMPv6 that targets for {}", destinationAddress);
+            }
         } catch (DeviceConfigNotFoundException e) {
-            log.warn(e.getMessage() + " Aborting processPacketIn.");
-            return;
-        }
-        ICMP6 icmp6 = (ICMP6) ipv6Packet.getPayload();
-        // ICMP to the router IP or gateway IP
-        if (icmp6.getIcmpType()  == ICMP6.ECHO_REQUEST &&
-                (destinationAddress.equals(routerIp.getIp6Address()) ||
-                        gatewayIpAddresses.contains(destinationAddress))) {
-            sendIcmpv6Response(eth, inPort);
-        } else {
-            log.trace("Ignore ICMPv6 that targets for {}", destinationAddress);
+            log.warn(e.getMessage() + " Ignore ICMPv6 that targets to {}.", destinationAddress);
         }
     }
 
@@ -256,11 +267,15 @@
             }
         }
 
-        int sid = config.getIPv6SegmentId(destRouterAddress);
-        if (sid < 0) {
-            log.warn("Failed to lookup SID of the switch that {} attaches to. " +
-                    "Unable to process ICMPv6 request.", destIpAddress);
-            return;
+        // Search SID only if store lookup is success otherwise proceed with "sid=-1"
+        int sid = -1;
+        if (destRouterAddress !=  null) {
+            sid = config.getIPv6SegmentId(destRouterAddress);
+            if (sid < 0) {
+                log.warn("Failed to lookup SID of the switch that {} attaches to. " +
+                        "Unable to process ICMPv6 request.", destIpAddress);
+                return;
+            }
         }
         sendPacketOut(outport, ethReply, sid, destIpAddress, icmpReplyIpv6.getHopLimit());
     }
@@ -301,16 +316,39 @@
 
     /**
      * Helper method to handle the ndp requests.
-     *
      * @param pkt the ndp packet request and context information
      * @param hostService the host service
      */
     private void handleNdpRequest(NeighbourMessageContext pkt, HostService hostService) {
         // ND request for the gateway. We have to reply on behalf of the gateway.
         if (isNdpForGateway(pkt)) {
-            log.trace("Sending NDP reply on behalf of gateway IP for pkt: {}", pkt);
-            sendResponse(pkt, config.getRouterMacForAGatewayIp(pkt.target()), hostService);
+            log.trace("Sending NDP reply on behalf of gateway IP for pkt: {}", pkt.target());
+            MacAddress routerMac = config.getRouterMacForAGatewayIp(pkt.target());
+            sendResponse(pkt, routerMac, hostService);
         } else {
+
+            // Process NDP targets towards EUI-64 address.
+            try {
+                DeviceId deviceId = pkt.inPort().deviceId();
+                Optional<MacAddress> macAddress = srManager.interfaceService.getInterfacesByPort(pkt.inPort())
+                        .stream()
+                        .map(Interface::mac)
+                        .findFirst();
+                if (!macAddress.isPresent()) {
+                    log.warn("Failed in fetching MAC address of {}. Aborting NDP processing.", pkt.inPort());
+                    return;
+                }
+                MacAddress interfaceMac = MacAddress.valueOf(macAddress.get().toBytes());
+                Ip6Address interfaceLinkLocalIP = Ip6Address.valueOf(IPv6.getLinkLocalAddress(interfaceMac.toBytes()));
+                if (pkt.target().equals(interfaceLinkLocalIP)) {
+                    MacAddress routerMac = config.getDeviceMac(deviceId);
+                    sendResponse(pkt, routerMac, hostService);
+                }
+            } catch (DeviceConfigNotFoundException e) {
+                log.warn(e.getMessage() + " Unable to handle NDP packet to {}. Aborting.", pkt.target());
+                return;
+            }
+
             // NOTE: Ignore NDP packets except those target for the router
             //       We will reconsider enabling this when we have host learning support
             /*
@@ -376,6 +414,7 @@
     private boolean isNdpForGateway(NeighbourMessageContext pkt) {
         DeviceId deviceId = pkt.inPort().deviceId();
         Set<IpAddress> gatewayIpAddresses = null;
+
         try {
             if (pkt.target().equals(config.getRouterIpv6(deviceId))) {
                 return true;
@@ -383,14 +422,13 @@
             gatewayIpAddresses = config.getPortIPs(deviceId);
         } catch (DeviceConfigNotFoundException e) {
             log.warn(e.getMessage() + " Aborting check for router IP in processing ndp");
+            return false;
         }
-
         return gatewayIpAddresses != null && gatewayIpAddresses.stream()
                 .filter(IpAddress::isIp6)
                 .anyMatch(gatewayIp -> gatewayIp.equals(pkt.target()) ||
                         Arrays.equals(IPv6.getSolicitNodeAddress(gatewayIp.toOctets()),
-                                pkt.target().toOctets())
-        );
+                                pkt.target().toOctets()));
     }
 
     /**
diff --git a/modules.defs b/modules.defs
index 389e91f..ffd756a 100644
--- a/modules.defs
+++ b/modules.defs
@@ -224,6 +224,7 @@
     '//apps/p4-tutorial/pipeconf:onos-apps-p4-tutorial-pipeconf-oar',
     '//apps/p4-tutorial/icmpdropper:onos-apps-p4-tutorial-icmpdropper-oar',
     '//apps/cfm:onos-apps-cfm-oar',
+    '//apps/routeradvertisement:onos-apps-routeradvertisement-oar',
 ]
 
 PROTOCOL_APPS = [