IPv6 support for vRouter

Changes:
- Adds support to CPRM;
- Updates DHM;
- Fixes SingleSwitchFibInstaller;
- Updates the driver;
- IPv6 unit tests;

Change-Id: I0d9a143fbf5ee8d77ffe3ed3e180fede200d3cdd
diff --git a/apps/routing/src/main/java/org/onosproject/routing/impl/ControlPlaneRedirectManager.java b/apps/routing/src/main/java/org/onosproject/routing/impl/ControlPlaneRedirectManager.java
index 5cd78a9..9a16bfd 100644
--- a/apps/routing/src/main/java/org/onosproject/routing/impl/ControlPlaneRedirectManager.java
+++ b/apps/routing/src/main/java/org/onosproject/routing/impl/ControlPlaneRedirectManager.java
@@ -17,6 +17,7 @@
 package org.onosproject.routing.impl;
 
 import com.google.common.collect.ImmutableSortedSet;
+import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
@@ -24,9 +25,17 @@
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.onlab.packet.EthType;
+import org.onlab.packet.Ip4Address;
 import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
+
+import static org.onlab.packet.Ethernet.TYPE_ARP;
+import static org.onlab.packet.Ethernet.TYPE_IPV4;
+import static org.onlab.packet.Ethernet.TYPE_IPV6;
+import static org.onlab.packet.ICMP6.NEIGHBOR_ADVERTISEMENT;
+import static org.onlab.packet.ICMP6.NEIGHBOR_SOLICITATION;
+import static org.onlab.packet.IPv6.PROTOCOL_ICMP6;
 import org.onosproject.app.ApplicationService;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
@@ -85,7 +94,7 @@
     private static final int MIN_IP_PRIORITY = 10;
     private static final int IPV4_PRIORITY = 2000;
     private static final int IPV6_PRIORITY = 500;
-    private static final int ACL_PRIORITY = 40001;
+    static final int ACL_PRIORITY = 40001;
     private static final int OSPF_IP_PROTO = 0x59;
 
     private static final String APP_NAME = "org.onosproject.vrouter";
@@ -228,59 +237,180 @@
                 intfNextId = modifyNextObjective(deviceId, intf.connectPoint().port(),
                                                  intf.vlan(), false, install);
             }
-
-            // IPv4 to router
-            TrafficSelector toSelector = DefaultTrafficSelector.builder()
-                    .matchInPort(intf.connectPoint().port())
-                    .matchEthDst(intf.mac())
-                    .matchEthType(EthType.EtherType.IPV4.ethType().toShort())
-                    .matchVlanId(intf.vlan())
-                    .matchIPDst(ip.ipAddress().toIpPrefix())
-                    .build();
-
-            flowObjectiveService.forward(deviceId,
-                    buildForwardingObjective(toSelector, null, cpNextId, install));
-
-            // IPv4 from router
-            TrafficSelector fromSelector = DefaultTrafficSelector.builder()
-                    .matchInPort(controlPlanePort)
-                    .matchEthSrc(intf.mac())
-                    .matchVlanId(intf.vlan())
-                    .matchEthType(EthType.EtherType.IPV4.ethType().toShort())
-                    .matchIPSrc(ip.ipAddress().toIpPrefix())
-                    .build();
-
-            flowObjectiveService.forward(deviceId,
-                    buildForwardingObjective(fromSelector, null, intfNextId, install));
-
-            // ARP to router
-            toSelector = DefaultTrafficSelector.builder()
-                    .matchInPort(intf.connectPoint().port())
-                    .matchEthType(EthType.EtherType.ARP.ethType().toShort())
-                    .matchVlanId(intf.vlan())
-                    .build();
-
-            TrafficTreatment puntTreatment = DefaultTrafficTreatment.builder()
+            List<ForwardingObjective> fwdToSend = Lists.newArrayList();
+            TrafficSelector selector;
+            // IP traffic toward the router.
+            selector = buildIPDstSelector(
+                    ip.ipAddress().toIpPrefix(),
+                    intf.connectPoint().port(),
+                    null,
+                    intf.mac(),
+                    intf.vlan()
+            );
+            fwdToSend.add(buildForwardingObjective(selector, null, cpNextId, install, ACL_PRIORITY));
+            // IP traffic from the router.
+            selector = buildIPSrcSelector(
+                    ip.ipAddress().toIpPrefix(),
+                    controlPlanePort,
+                    intf.mac(),
+                    null,
+                    intf.vlan()
+            );
+            fwdToSend.add(buildForwardingObjective(selector, null, intfNextId, install, ACL_PRIORITY));
+            // We build the punt treatment.
+            TrafficTreatment treatment = DefaultTrafficTreatment.builder()
                     .punt()
                     .build();
-
-            flowObjectiveService.forward(deviceId,
-                    buildForwardingObjective(toSelector, puntTreatment, cpNextId, install));
-
-            // ARP from router
-            fromSelector = DefaultTrafficSelector.builder()
-                    .matchInPort(controlPlanePort)
-                    .matchEthSrc(intf.mac())
-                    .matchVlanId(intf.vlan())
-                    .matchEthType(EthType.EtherType.ARP.ethType().toShort())
-                    .matchArpSpa(ip.ipAddress().getIp4Address())
-                    .build();
-
-            flowObjectiveService.forward(deviceId,
-            buildForwardingObjective(fromSelector, puntTreatment, intfNextId, install));
+            // Handling of neighbour discovery protocols.
+            // IPv4 traffic - we have to deal with the ARP protocol.
+            // IPv6 traffic - we have to deal with the NDP protocol.
+            if (ip.ipAddress().isIp4()) {
+                 // ARP traffic towards the router.
+                selector = buildArpSelector(
+                        intf.connectPoint().port(),
+                        intf.vlan(),
+                        null,
+                        null
+                );
+                fwdToSend.add(buildForwardingObjective(selector, treatment, cpNextId, install, ACL_PRIORITY + 1));
+                // ARP traffic from the router.
+                selector = buildArpSelector(
+                        controlPlanePort,
+                        intf.vlan(),
+                        ip.ipAddress().getIp4Address(),
+                        intf.mac()
+                );
+                fwdToSend.add(buildForwardingObjective(selector, treatment, intfNextId, install, ACL_PRIORITY + 1));
+            } else {
+                // Neighbour solicitation traffic towards the router.
+                selector = buildNdpSelector(
+                        intf.connectPoint().port(),
+                        intf.vlan(),
+                        null,
+                        NEIGHBOR_SOLICITATION,
+                        null
+                );
+                fwdToSend.add(buildForwardingObjective(selector, treatment, cpNextId, install, ACL_PRIORITY + 1));
+                // Neighbour solicitation traffic from the router.
+                selector = buildNdpSelector(
+                        controlPlanePort,
+                        intf.vlan(),
+                        ip.ipAddress().toIpPrefix(),
+                        NEIGHBOR_SOLICITATION,
+                        intf.mac()
+                );
+                fwdToSend.add(buildForwardingObjective(selector, treatment, intfNextId, install, ACL_PRIORITY + 1));
+                 // Neighbour advertisement traffic towards the router.
+                selector = buildNdpSelector(
+                        intf.connectPoint().port(),
+                        intf.vlan(),
+                        null,
+                        NEIGHBOR_ADVERTISEMENT,
+                        null
+                );
+                fwdToSend.add(buildForwardingObjective(selector, treatment, cpNextId, install, ACL_PRIORITY + 1));
+                // Neighbour advertisement traffic from the router.
+                selector = buildNdpSelector(
+                        controlPlanePort,
+                        intf.vlan(),
+                        ip.ipAddress().toIpPrefix(),
+                        NEIGHBOR_ADVERTISEMENT,
+                        intf.mac()
+                );
+                fwdToSend.add(buildForwardingObjective(selector, treatment, intfNextId, install, ACL_PRIORITY + 1));
+            }
+            // Finally we push the fwd objectives through the flow objective service.
+            fwdToSend.stream().forEach(forwardingObjective ->
+                flowObjectiveService.forward(deviceId, forwardingObjective)
+            );
         }
     }
 
+    static TrafficSelector.Builder buildBaseSelectorBuilder(PortNumber inPort,
+                                                             MacAddress srcMac,
+                                                             MacAddress dstMac,
+                                                             VlanId vlanId) {
+        TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
+        if (inPort != null) {
+            selectorBuilder.matchInPort(inPort);
+        }
+        if (srcMac != null) {
+            selectorBuilder.matchEthSrc(srcMac);
+        }
+        if (dstMac != null) {
+            selectorBuilder.matchEthDst(dstMac);
+        }
+        if (vlanId != null) {
+            selectorBuilder.matchVlanId(vlanId);
+        }
+        return selectorBuilder;
+    }
+
+    static TrafficSelector buildIPDstSelector(IpPrefix dstIp,
+                                               PortNumber inPort,
+                                               MacAddress srcMac,
+                                               MacAddress dstMac,
+                                               VlanId vlanId) {
+        TrafficSelector.Builder selector = buildBaseSelectorBuilder(inPort, srcMac, dstMac, vlanId);
+        if (dstIp.isIp4()) {
+            selector.matchEthType(TYPE_IPV4);
+            selector.matchIPDst(dstIp);
+        } else {
+            selector.matchEthType(TYPE_IPV6);
+            selector.matchIPv6Dst(dstIp);
+        }
+        return selector.build();
+    }
+
+    static TrafficSelector buildIPSrcSelector(IpPrefix srcIp,
+                                               PortNumber inPort,
+                                               MacAddress srcMac,
+                                               MacAddress dstMac,
+                                               VlanId vlanId) {
+        TrafficSelector.Builder selector = buildBaseSelectorBuilder(inPort, srcMac, dstMac, vlanId);
+        if (srcIp.isIp4()) {
+            selector.matchEthType(TYPE_IPV4);
+            selector.matchIPSrc(srcIp);
+        } else {
+            selector.matchEthType(TYPE_IPV6);
+            selector.matchIPv6Src(srcIp);
+        }
+        return selector.build();
+    }
+
+    static TrafficSelector buildArpSelector(PortNumber inPort,
+                                             VlanId vlanId,
+                                             Ip4Address arpSpa,
+                                             MacAddress srcMac) {
+        TrafficSelector.Builder selector = buildBaseSelectorBuilder(inPort, null, null, vlanId);
+        selector.matchEthType(TYPE_ARP);
+        if (arpSpa != null) {
+            selector.matchArpSpa(arpSpa);
+        }
+        if (srcMac != null) {
+            selector.matchEthSrc(srcMac);
+        }
+        return selector.build();
+    }
+
+    static TrafficSelector buildNdpSelector(PortNumber inPort,
+                                             VlanId vlanId,
+                                             IpPrefix srcIp,
+                                             byte subProto,
+                                             MacAddress srcMac) {
+        TrafficSelector.Builder selector = buildBaseSelectorBuilder(inPort, null, null, vlanId);
+        selector.matchEthType(TYPE_IPV6)
+                .matchIPProtocol(PROTOCOL_ICMP6)
+                .matchIcmpv6Type(subProto);
+        if (srcIp != null) {
+            selector.matchIPv6Src(srcIp);
+        }
+        if (srcMac != null) {
+            selector.matchEthSrc(srcMac);
+        }
+        return selector.build();
+    }
+
     /**
      * Installs or removes OSPF forwarding rules.
      *
@@ -289,6 +419,7 @@
      *            objective
      **/
     private void updateOspfForwarding(Interface intf, boolean install) {
+        // FIXME IPv6 support has not been implemented yet
         // OSPF to router
         TrafficSelector toSelector = DefaultTrafficSelector.builder()
                 .matchInPort(intf.connectPoint().port())
@@ -311,7 +442,7 @@
         }
         log.debug("OSPF flows intf:{} nextid:{}", intf, cpNextId);
         flowObjectiveService.forward(controlPlaneConnectPoint.deviceId(),
-                buildForwardingObjective(toSelector, null, cpNextId, install ? ospfEnabled : install));
+                buildForwardingObjective(toSelector, null, cpNextId, install ? ospfEnabled : install, ACL_PRIORITY));
     }
 
     /**
@@ -370,7 +501,8 @@
     private ForwardingObjective buildForwardingObjective(TrafficSelector selector,
                                                          TrafficTreatment treatment,
                                                          int nextId,
-                                                         boolean add) {
+                                                         boolean add,
+                                                         int priority) {
         DefaultForwardingObjective.Builder fobBuilder = DefaultForwardingObjective.builder();
         fobBuilder.withSelector(selector);
         if (treatment != null) {
@@ -380,7 +512,7 @@
             fobBuilder.nextStep(nextId);
         }
         fobBuilder.fromApp(appId)
-            .withPriority(ACL_PRIORITY)
+            .withPriority(priority)
             .withFlag(ForwardingObjective.Flag.VERSATILE);
 
         return add ? fobBuilder.add() : fobBuilder.remove();
@@ -425,22 +557,22 @@
         public void event(NetworkConfigEvent event) {
             if (event.configClass().equals(RoutingService.ROUTER_CONFIG_CLASS)) {
                 switch (event.type()) {
-                    case CONFIG_ADDED:
-                    case CONFIG_UPDATED:
-                        readConfig();
-                        if (event.prevConfig().isPresent()) {
+                case CONFIG_ADDED:
+                case CONFIG_UPDATED:
+                    readConfig();
+                    if (event.prevConfig().isPresent()) {
                             updateConfig(event);
                             }
 
-                        break;
-                    case CONFIG_REGISTERED:
-                    case CONFIG_UNREGISTERED:
-                    case CONFIG_REMOVED:
-                        removeConfig();
+                    break;
+                case CONFIG_REGISTERED:
+                case CONFIG_UNREGISTERED:
+                case CONFIG_REMOVED:
+                    removeConfig();
 
                         break;
-                    default:
-                        break;
+                default:
+                    break;
                 }
             }
         }
@@ -520,12 +652,10 @@
 
         private ForwardingObjective.Builder createPeerObjBuilder(
                 int nextId, IpPrefix ipAddresses) {
-            TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
-            sbuilder.matchEthType(EthType.EtherType.IPV4.ethType().toShort());
-            sbuilder.matchIPDst(ipAddresses);
+            TrafficSelector selector = buildIPDstSelector(ipAddresses, null, null, null, null);
             DefaultForwardingObjective.Builder builder =
                     DefaultForwardingObjective.builder()
-                    .withSelector(sbuilder.build())
+                    .withSelector(selector)
                     .fromApp(appId)
                     .withPriority(getPriorityFromPrefix(ipAddresses))
                     .withFlag(ForwardingObjective.Flag.SPECIFIC);
@@ -577,7 +707,8 @@
                     peerRemoved(event);
                     break;
                 case HOST_UPDATED:
-                    //TODO We assume BGP peer does not change IP for now
+                    //FIXME We assume BGP peer does not change IP for now
+                    // but we can discover new address.
                     break;
                 default:
                     break;
@@ -628,11 +759,10 @@
     }
 
     private Set<Interface> filterInterfaces(List<String> interfaces) {
-        Set<Interface> intfs = interfaceService.getInterfaces().stream()
+        return interfaceService.getInterfaces().stream()
                 .filter(intf -> intf.connectPoint().deviceId().equals(controlPlaneConnectPoint.deviceId()))
                 .filter(intf -> interfaces.contains(intf.name()))
                 .collect(Collectors.toSet());
-        return intfs;
     }
 
     private void removeConfig() {
@@ -707,3 +837,4 @@
         }
     }
 }
+
diff --git a/apps/routing/src/main/java/org/onosproject/routing/impl/DirectHostManager.java b/apps/routing/src/main/java/org/onosproject/routing/impl/DirectHostManager.java
index d5c0d27..a1a7d25 100644
--- a/apps/routing/src/main/java/org/onosproject/routing/impl/DirectHostManager.java
+++ b/apps/routing/src/main/java/org/onosproject/routing/impl/DirectHostManager.java
@@ -28,7 +28,8 @@
 import org.onlab.packet.EthType;
 import org.onlab.packet.Ethernet;
 import org.onlab.packet.IPv4;
-import org.onlab.packet.Ip4Address;
+import org.onlab.packet.IPv6;
+import org.onlab.packet.IP;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
@@ -63,6 +64,7 @@
 import java.util.concurrent.TimeUnit;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.packet.IpAddress.Version.INET6;
 
 /**
  * Reactively handles sending packets to hosts that are directly connected to
@@ -104,8 +106,8 @@
     private InternalPacketProcessor packetProcessor = new InternalPacketProcessor();
     private InternalHostListener hostListener = new InternalHostListener();
 
-    private Cache<IpAddress, Queue<IPv4>> ipPacketCache = CacheBuilder.newBuilder()
-            .weigher((IpAddress key, Queue<IPv4> value) -> value.size())
+    private Cache<IpAddress, Queue<IP>> ipPacketCache = CacheBuilder.newBuilder()
+            .weigher((IpAddress key, Queue<IP> value) -> value.size())
             .maximumWeight(MAX_QUEUED_PACKETS)
             .expireAfterAccess(MAX_QUEUE_DURATION, TimeUnit.SECONDS)
             .build();
@@ -134,19 +136,27 @@
     private void enable() {
         hostService.addListener(hostListener);
         packetService.addProcessor(packetProcessor, PacketProcessor.director(3));
-
+        // Requests packets for IPv4 traffic.
         TrafficSelector selector = DefaultTrafficSelector.builder()
                 .matchEthType(EthType.EtherType.IPV4.ethType().toShort()).build();
         packetService.requestPackets(selector, PacketPriority.REACTIVE, appId, Optional.empty());
+        // Requests packets for IPv6 traffic.
+        selector = DefaultTrafficSelector.builder()
+                .matchEthType(EthType.EtherType.IPV6.ethType().toShort()).build();
+        packetService.requestPackets(selector, PacketPriority.REACTIVE, appId, Optional.empty());
     }
 
     private void disable() {
         packetService.removeProcessor(packetProcessor);
         hostService.removeListener(hostListener);
-
+        // Withdraws IPv4 request.
         TrafficSelector selector = DefaultTrafficSelector.builder()
                 .matchEthType(EthType.EtherType.IPV4.ethType().toShort()).build();
         packetService.cancelPackets(selector, PacketPriority.REACTIVE, appId, Optional.empty());
+        // Withdraws IPv6 request.
+        selector = DefaultTrafficSelector.builder()
+                .matchEthType(EthType.EtherType.IPV6.ethType().toShort()).build();
+        packetService.cancelPackets(selector, PacketPriority.REACTIVE, appId, Optional.empty());
     }
 
     @Deactivate
@@ -158,57 +168,82 @@
         componentConfigService.unregisterProperties(getClass(), false);
     }
 
-    private void handle(Ethernet eth) {
+    private boolean handle(Ethernet eth) {
         checkNotNull(eth);
-
-        if (!(eth.getEtherType() == EthType.EtherType.IPV4.ethType().toShort())) {
-            return;
+        // If the DirectHostManager is not enabled and the
+        // packets are different from what we expect just
+        // skip them.
+        if (!enabled || (eth.getEtherType() != Ethernet.TYPE_IPV6
+                && eth.getEtherType() != Ethernet.TYPE_IPV4)) {
+            return false;
         }
-
-        IPv4 ipv4 = (IPv4) eth.getPayload().clone();
-
-        Ip4Address dstIp = Ip4Address.valueOf(ipv4.getDestinationAddress());
-
+        // According to the type we set the destIp.
+        IpAddress dstIp;
+        if (eth.getEtherType() == Ethernet.TYPE_IPV4) {
+            IPv4 ip = (IPv4) eth.getPayload();
+            dstIp = IpAddress.valueOf(ip.getDestinationAddress());
+        } else {
+            IPv6 ip = (IPv6) eth.getPayload();
+            dstIp = IpAddress.valueOf(INET6, ip.getDestinationAddress());
+        }
+        // Looking for a candidate output port.
         Interface egressInterface = interfaceService.getMatchingInterface(dstIp);
 
         if (egressInterface == null) {
             log.info("No egress interface found for {}", dstIp);
-            return;
+            return false;
         }
-
+        // Looking for the destination mac.
         Optional<Host> host = hostService.getHostsByIp(dstIp).stream()
                 .filter(h -> h.location().equals(egressInterface.connectPoint()))
                 .filter(h -> h.vlan().equals(egressInterface.vlan()))
                 .findAny();
-
+        // If we don't have a destination we start the monitoring
+        // and we queue the packets waiting for a destination.
         if (host.isPresent()) {
-            transformAndSend(ipv4, egressInterface, host.get().mac());
+            transformAndSend(
+                    (IP) eth.getPayload(),
+                    eth.getEtherType(),
+                    egressInterface,
+                    host.get().mac()
+            );
         } else {
             hostService.startMonitoringIp(dstIp);
             ipPacketCache.asMap().compute(dstIp, (ip, queue) -> {
                 if (queue == null) {
-                    queue = new ConcurrentLinkedQueue();
+                    queue = new ConcurrentLinkedQueue<>();
                 }
-                queue.add(ipv4);
+                queue.add((IP) eth.getPayload());
                 return queue;
             });
         }
+
+        return true;
     }
 
-    private void transformAndSend(IPv4 ipv4, Interface egressInterface, MacAddress macAddress) {
-
+    private void transformAndSend(IP ip, short ethType,
+                                  Interface egressInterface,
+                                  MacAddress macAddress) {
+        // Base processing for IPv4
+        if (ethType == Ethernet.TYPE_IPV4) {
+            IPv4 ipv4 = (IPv4) ip;
+            ipv4.setTtl((byte) (ipv4.getTtl() - 1));
+            ipv4.setChecksum((short) 0);
+        // Base processing for IPv6.
+        } else {
+            IPv6 ipv6 = (IPv6) ip;
+            ipv6.setHopLimit((byte) (ipv6.getHopLimit() - 1));
+            ipv6.resetChecksum();
+        }
+        // Sends and serializes.
         Ethernet eth = new Ethernet();
         eth.setDestinationMACAddress(macAddress);
         eth.setSourceMACAddress(egressInterface.mac());
-        eth.setEtherType(EthType.EtherType.IPV4.ethType().toShort());
-        eth.setPayload(ipv4);
+        eth.setEtherType(ethType);
+        eth.setPayload(ip);
         if (!egressInterface.vlan().equals(VlanId.NONE)) {
             eth.setVlanID(egressInterface.vlan().toShort());
         }
-
-        ipv4.setTtl((byte) (ipv4.getTtl() - 1));
-        ipv4.setChecksum((short) 0);
-
         send(eth, egressInterface.connectPoint());
     }
 
@@ -221,7 +256,7 @@
     private void sendQueued(IpAddress ipAddress, MacAddress macAddress) {
         log.debug("Sending queued packets for {} ({})", ipAddress, macAddress);
         ipPacketCache.asMap().computeIfPresent(ipAddress, (ip, packets) -> {
-            packets.forEach(ipv4 -> {
+            packets.forEach(ipPackets -> {
                 Interface egressInterface = interfaceService.getMatchingInterface(ipAddress);
 
                 if (egressInterface == null) {
@@ -229,7 +264,14 @@
                     return;
                 }
 
-                transformAndSend(ipv4, egressInterface, macAddress);
+                // According to the type of the address we set proper
+                // protocol.
+                transformAndSend(
+                        ipPackets,
+                        ipAddress.isIp4() ? Ethernet.TYPE_IPV4 : Ethernet.TYPE_IPV6,
+                        egressInterface,
+                        macAddress
+                );
             });
             return null;
         });
@@ -253,7 +295,9 @@
                 return;
             }
 
-            handle(eth);
+            if (!handle(eth)) {
+                return;
+            }
 
             context.block();
         }
diff --git a/apps/routing/src/main/java/org/onosproject/routing/impl/SingleSwitchFibInstaller.java b/apps/routing/src/main/java/org/onosproject/routing/impl/SingleSwitchFibInstaller.java
index f0fe3fe..24dd468 100644
--- a/apps/routing/src/main/java/org/onosproject/routing/impl/SingleSwitchFibInstaller.java
+++ b/apps/routing/src/main/java/org/onosproject/routing/impl/SingleSwitchFibInstaller.java
@@ -126,7 +126,7 @@
     protected ApplicationService applicationService;
 
     @Property(name = "routeToNextHop", boolValue = false,
-            label = "Install a /32 route to each next hop")
+            label = "Install a /32 or /128 route to each next hop")
     private boolean routeToNextHop = false;
 
     // Device id of data-plane switch - should be learned from config
@@ -333,11 +333,7 @@
 
     private ForwardingObjective.Builder generateRibForwardingObj(IpPrefix prefix,
                                                                  Integer nextId) {
-        TrafficSelector selector = DefaultTrafficSelector.builder()
-                .matchEthType(Ethernet.TYPE_IPV4)
-                .matchIPDst(prefix)
-                .build();
-
+        TrafficSelector selector = buildIpSelectorFromIpPrefix(prefix).build();
         int priority = prefix.prefixLength() * PRIORITY_MULTIPLIER + PRIORITY_OFFSET;
 
         ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective.builder()
@@ -357,11 +353,30 @@
         return fwdBuilder;
     }
 
+    /**
+     * Method to build IPv4 or IPv6 selector.
+     *
+     * @param prefixToMatch the prefix to match
+     * @return the traffic selector builder
+     */
+    private TrafficSelector.Builder buildIpSelectorFromIpPrefix(IpPrefix prefixToMatch) {
+        TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
+        // If the prefix is IPv4
+        if (prefixToMatch.isIp4()) {
+            selectorBuilder.matchEthType(Ethernet.TYPE_IPV4);
+            selectorBuilder.matchIPDst(prefixToMatch);
+            return selectorBuilder;
+        }
+        // If the prefix is IPv6
+        selectorBuilder.matchEthType(Ethernet.TYPE_IPV6);
+        selectorBuilder.matchIPv6Dst(prefixToMatch);
+        return selectorBuilder;
+    }
+
     private synchronized void addNextHop(ResolvedRoute route) {
         prefixToNextHop.put(route.prefix(), route.nextHop());
         if (nextHopsCount.count(route.nextHop()) == 0) {
             // There was no next hop in the multiset
-
             Interface egressIntf = interfaceService.getMatchingInterface(route.nextHop());
             if (egressIntf == null) {
                 log.warn("no egress interface found for {}", route);