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);
diff --git a/apps/routing/src/test/java/org/onosproject/routing/impl/ControlPlaneRedirectManagerTest.java b/apps/routing/src/test/java/org/onosproject/routing/impl/ControlPlaneRedirectManagerTest.java
index 08e89e7..5d07541 100644
--- a/apps/routing/src/test/java/org/onosproject/routing/impl/ControlPlaneRedirectManagerTest.java
+++ b/apps/routing/src/test/java/org/onosproject/routing/impl/ControlPlaneRedirectManagerTest.java
@@ -76,6 +76,9 @@
 import static org.easymock.EasyMock.expectLastCall;
 import static org.easymock.EasyMock.replay;
 import static org.easymock.EasyMock.verify;
+import static org.onlab.packet.ICMP6.NEIGHBOR_ADVERTISEMENT;
+import static org.onlab.packet.ICMP6.NEIGHBOR_SOLICITATION;
+import static org.onosproject.routing.impl.ControlPlaneRedirectManager.*;
 
 /**
  * UnitTests for ControlPlaneRedirectManager.
@@ -149,11 +152,15 @@
     @Test
     public void testAddDevice() {
         ConnectPoint sw1eth4 = new ConnectPoint(DEVICE_ID, PortNumber.portNumber(4));
-        List<InterfaceIpAddress> interfaceIpAddresses4 = new ArrayList<>();
-        interfaceIpAddresses4
-                .add(new InterfaceIpAddress(IpAddress.valueOf("192.168.40.101"), IpPrefix.valueOf("192.168.40.0/24")));
+        List<InterfaceIpAddress> interfaceIpAddresses = new ArrayList<>();
+        interfaceIpAddresses.add(
+                new InterfaceIpAddress(IpAddress.valueOf("192.168.40.101"), IpPrefix.valueOf("192.168.40.0/24"))
+        );
+        interfaceIpAddresses.add(
+                new InterfaceIpAddress(IpAddress.valueOf("2000::ff"), IpPrefix.valueOf("2000::ff/120"))
+        );
 
-        Interface sw1Eth4 = new Interface(sw1eth4.deviceId().toString(), sw1eth4, interfaceIpAddresses4,
+        Interface sw1Eth4 = new Interface(sw1eth4.deviceId().toString(), sw1eth4, interfaceIpAddresses,
                 MacAddress.valueOf("00:00:00:00:00:04"), VlanId.NONE);
         interfaces.add(sw1Eth4);
         EasyMock.reset(flowObjectiveService);
@@ -168,11 +175,15 @@
     @Test
     public void testUpdateNetworkConfig() {
         ConnectPoint sw1eth4 = new ConnectPoint(DEVICE_ID, PortNumber.portNumber(4));
-        List<InterfaceIpAddress> interfaceIpAddresses4 = new ArrayList<>();
-        interfaceIpAddresses4
-                .add(new InterfaceIpAddress(IpAddress.valueOf("192.168.40.101"), IpPrefix.valueOf("192.168.40.0/24")));
+        List<InterfaceIpAddress> interfaceIpAddresses = new ArrayList<>();
+        interfaceIpAddresses.add(
+                new InterfaceIpAddress(IpAddress.valueOf("192.168.40.101"), IpPrefix.valueOf("192.168.40.0/24"))
+        );
+        interfaceIpAddresses.add(
+                new InterfaceIpAddress(IpAddress.valueOf("2000::ff"), IpPrefix.valueOf("2000::ff/120"))
+        );
 
-        Interface sw1Eth4 = new Interface(sw1eth4.deviceId().toString(), sw1eth4, interfaceIpAddresses4,
+        Interface sw1Eth4 = new Interface(sw1eth4.deviceId().toString(), sw1eth4, interfaceIpAddresses,
                 MacAddress.valueOf("00:00:00:00:00:04"), VlanId.NONE);
         interfaces.add(sw1Eth4);
         EasyMock.reset(flowObjectiveService);
@@ -189,11 +200,15 @@
     @Test
     public void testAddInterface() {
         ConnectPoint sw1eth4 = new ConnectPoint(DEVICE_ID, PortNumber.portNumber(4));
-        List<InterfaceIpAddress> interfaceIpAddresses4 = new ArrayList<>();
-        interfaceIpAddresses4
-                .add(new InterfaceIpAddress(IpAddress.valueOf("192.168.40.101"), IpPrefix.valueOf("192.168.40.0/24")));
+        List<InterfaceIpAddress> interfaceIpAddresses = new ArrayList<>();
+        interfaceIpAddresses.add(
+                new InterfaceIpAddress(IpAddress.valueOf("192.168.40.101"), IpPrefix.valueOf("192.168.40.0/24"))
+        );
+        interfaceIpAddresses.add(
+                new InterfaceIpAddress(IpAddress.valueOf("2000::ff"), IpPrefix.valueOf("2000::ff/120"))
+        );
 
-        Interface sw1Eth4 = new Interface(sw1eth4.deviceId().toString(), sw1eth4, interfaceIpAddresses4,
+        Interface sw1Eth4 = new Interface(sw1eth4.deviceId().toString(), sw1eth4, interfaceIpAddresses,
                 MacAddress.valueOf("00:00:00:00:00:04"), VlanId.NONE);
         interfaces.add(sw1Eth4);
 
@@ -209,11 +224,15 @@
     @Test
     public void testRemoveInterface() {
         ConnectPoint sw1eth4 = new ConnectPoint(DEVICE_ID, PortNumber.portNumber(4));
-        List<InterfaceIpAddress> interfaceIpAddresses4 = new ArrayList<>();
-        interfaceIpAddresses4
-                .add(new InterfaceIpAddress(IpAddress.valueOf("192.168.40.101"), IpPrefix.valueOf("192.168.40.0/24")));
+        List<InterfaceIpAddress> interfaceIpAddresses = new ArrayList<>();
+        interfaceIpAddresses.add(
+                new InterfaceIpAddress(IpAddress.valueOf("192.168.40.101"), IpPrefix.valueOf("192.168.40.0/24"))
+        );
+        interfaceIpAddresses.add(
+                new InterfaceIpAddress(IpAddress.valueOf("2000::ff"), IpPrefix.valueOf("2000::ff/120"))
+        );
 
-        Interface sw1Eth4 = new Interface(sw1eth4.deviceId().toString(), sw1eth4, interfaceIpAddresses4,
+        Interface sw1Eth4 = new Interface(sw1eth4.deviceId().toString(), sw1eth4, interfaceIpAddresses,
                 MacAddress.valueOf("00:00:00:00:00:04"), VlanId.NONE);
         EasyMock.reset(flowObjectiveService);
         expect(flowObjectiveService.allocateNextId()).andReturn(1).anyTimes();
@@ -251,38 +270,110 @@
             intfNextId = modifyNextObjective(deviceId, intf.connectPoint().port(),
                     VlanId.vlanId(SingleSwitchFibInstaller.ASSIGNED_VLAN), true, 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();
+            // IP to router
+            TrafficSelector toSelector = buildIPDstSelector(
+                    ip.ipAddress().toIpPrefix(),
+                    intf.connectPoint().port(),
+                    null,
+                    intf.mac(),
+                    intf.vlan());
 
-            flowObjectiveService.forward(deviceId, buildForwardingObjective(toSelector, null, cpNextId, install));
+            flowObjectiveService.forward(deviceId, buildForwardingObjective(toSelector, null,
+                                                                            cpNextId, install, ACL_PRIORITY));
             expectLastCall().once();
-            // 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));
+            // IP from router
+            TrafficSelector fromSelector = buildIPSrcSelector(
+                    ip.ipAddress().toIpPrefix(),
+                    controlPlanePort,
+                    intf.mac(),
+                    null,
+                    intf.vlan()
+            );
+
+            flowObjectiveService.forward(deviceId, buildForwardingObjective(fromSelector, null,
+                                                                            intfNextId, install, ACL_PRIORITY));
             expectLastCall().once();
-            // ARP to router
-            toSelector = DefaultTrafficSelector.builder().matchInPort(intf.connectPoint().port())
-                    .matchEthType(EthType.EtherType.ARP.ethType().toShort()).matchVlanId(intf.vlan()).build();
 
             TrafficTreatment puntTreatment = DefaultTrafficTreatment.builder().punt().build();
+            if (ip.ipAddress().isIp4()) {
+                // ARP to router
+                toSelector = buildArpSelector(
+                        intf.connectPoint().port(),
+                        intf.vlan(),
+                        null,
+                        null
+                );
 
-            flowObjectiveService.forward(deviceId,
-                    buildForwardingObjective(toSelector, puntTreatment, cpNextId, install));
-            expectLastCall().once();
-            // 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(toSelector, puntTreatment,
+                                                                                cpNextId, install, ACL_PRIORITY + 1));
+                expectLastCall().once();
 
-            flowObjectiveService.forward(deviceId,
-                    buildForwardingObjective(fromSelector, puntTreatment, intfNextId, install));
-            expectLastCall().once();
+                // ARP from router
+                fromSelector = buildArpSelector(
+                        controlPlanePort,
+                        intf.vlan(),
+                        ip.ipAddress().getIp4Address(),
+                        intf.mac()
+                );
+
+                flowObjectiveService.forward(deviceId,
+                                             buildForwardingObjective(fromSelector, puntTreatment,
+                                                                      intfNextId, install, ACL_PRIORITY + 1));
+                expectLastCall().once();
+            } else {
+                // NDP solicitation to router
+                toSelector = buildNdpSelector(
+                        intf.connectPoint().port(),
+                        intf.vlan(),
+                        null,
+                        NEIGHBOR_SOLICITATION,
+                        null
+                );
+
+                flowObjectiveService.forward(deviceId, buildForwardingObjective(toSelector, puntTreatment,
+                                                                                cpNextId, install, ACL_PRIORITY + 1));
+                expectLastCall().once();
+
+                // NDP solicitation from router
+                fromSelector = buildNdpSelector(
+                        controlPlanePort,
+                        intf.vlan(),
+                        ip.ipAddress().toIpPrefix(),
+                        NEIGHBOR_SOLICITATION,
+                        intf.mac()
+                );
+
+                flowObjectiveService.forward(deviceId,
+                                             buildForwardingObjective(fromSelector, puntTreatment,
+                                                                      intfNextId, install, ACL_PRIORITY + 1));
+
+                // NDP advertisement to router
+                toSelector = buildNdpSelector(
+                        intf.connectPoint().port(),
+                        intf.vlan(),
+                        null,
+                        NEIGHBOR_ADVERTISEMENT,
+                        null
+                );
+
+                flowObjectiveService.forward(deviceId, buildForwardingObjective(toSelector, puntTreatment,
+                                                                                cpNextId, install, ACL_PRIORITY + 1));
+                expectLastCall().once();
+
+                // NDP advertisement from router
+                fromSelector = buildNdpSelector(
+                        controlPlanePort,
+                        intf.vlan(),
+                        ip.ipAddress().toIpPrefix(),
+                        NEIGHBOR_ADVERTISEMENT,
+                        intf.mac()
+                );
+
+                flowObjectiveService.forward(deviceId,
+                                             buildForwardingObjective(fromSelector, puntTreatment,
+                                                                      intfNextId, install, ACL_PRIORITY + 1));
+            }
         }
         // setting expectations for ospf forwarding.
         TrafficSelector toSelector = DefaultTrafficSelector.builder().matchInPort(intf.connectPoint().port())
@@ -291,7 +382,7 @@
 
         modifyNextObjective(deviceId, controlPlanePort, VlanId.vlanId((short) 4094), true, install);
         flowObjectiveService.forward(controlPlaneConnectPoint.deviceId(),
-                buildForwardingObjective(toSelector, null, 1, install));
+                buildForwardingObjective(toSelector, null, 1, install, 40001));
         expectLastCall().once();
     }
 
@@ -353,7 +444,7 @@
     }
 
     private ForwardingObjective buildForwardingObjective(TrafficSelector selector, TrafficTreatment treatment,
-            int nextId, boolean add) {
+            int nextId, boolean add, int priority) {
         DefaultForwardingObjective.Builder fobBuilder = DefaultForwardingObjective.builder();
         fobBuilder.withSelector(selector);
         if (treatment != null) {
@@ -362,7 +453,7 @@
         if (nextId != -1) {
             fobBuilder.nextStep(nextId);
         }
-        fobBuilder.fromApp(APPID).withPriority(40001).withFlag(ForwardingObjective.Flag.VERSATILE);
+        fobBuilder.fromApp(APPID).withPriority(priority).withFlag(ForwardingObjective.Flag.VERSATILE);
 
         return add ? fobBuilder.add() : fobBuilder.remove();
     }
diff --git a/drivers/default/src/main/java/org/onosproject/driver/pipeline/SoftRouterPipeline.java b/drivers/default/src/main/java/org/onosproject/driver/pipeline/SoftRouterPipeline.java
index 66d0b53..ac9c38a 100644
--- a/drivers/default/src/main/java/org/onosproject/driver/pipeline/SoftRouterPipeline.java
+++ b/drivers/default/src/main/java/org/onosproject/driver/pipeline/SoftRouterPipeline.java
@@ -43,6 +43,8 @@
 import org.onosproject.net.flow.criteria.EthCriterion;
 import org.onosproject.net.flow.criteria.EthTypeCriterion;
 import org.onosproject.net.flow.criteria.IPCriterion;
+import org.onosproject.net.flow.criteria.IPProtocolCriterion;
+import org.onosproject.net.flow.criteria.Icmpv6TypeCriterion;
 import org.onosproject.net.flow.criteria.PortCriterion;
 import org.onosproject.net.flow.criteria.VlanIdCriterion;
 import org.onosproject.net.flow.instructions.Instruction;
@@ -62,6 +64,10 @@
 import java.util.List;
 import java.util.Objects;
 
+import static org.onlab.packet.Ethernet.TYPE_IPV4;
+import static org.onlab.packet.ICMP6.ECHO_REPLY;
+import static org.onlab.packet.ICMP6.ECHO_REQUEST;
+import static org.onlab.packet.IPv6.PROTOCOL_ICMP6;
 import static org.onlab.util.Tools.delay;
 import static org.slf4j.LoggerFactory.getLogger;
 
@@ -158,25 +164,25 @@
     @Override
     public void next(NextObjective nextObjective) {
         switch (nextObjective.type()) {
-        case SIMPLE:
-            Collection<TrafficTreatment> treatments = nextObjective.next();
-            if (treatments.size() != 1) {
-                log.error("Next Objectives of type Simple should only have a "
-                        + "single Traffic Treatment. Next Objective Id:{}", nextObjective.id());
-               fail(nextObjective, ObjectiveError.BADPARAMS);
-               return;
-            }
-            processSimpleNextObjective(nextObjective);
-            break;
-        case HASHED:
-        case BROADCAST:
-        case FAILOVER:
-            fail(nextObjective, ObjectiveError.UNSUPPORTED);
-            log.warn("Unsupported next objective type {}", nextObjective.type());
-            break;
-        default:
-            fail(nextObjective, ObjectiveError.UNKNOWN);
-            log.warn("Unknown next objective type {}", nextObjective.type());
+            case SIMPLE:
+                Collection<TrafficTreatment> treatments = nextObjective.next();
+                if (treatments.size() != 1) {
+                    log.error("Next Objectives of type Simple should only have a "
+                                      + "single Traffic Treatment. Next Objective Id:{}", nextObjective.id());
+                    fail(nextObjective, ObjectiveError.BADPARAMS);
+                    return;
+                }
+                processSimpleNextObjective(nextObjective);
+                break;
+            case HASHED:
+            case BROADCAST:
+            case FAILOVER:
+                fail(nextObjective, ObjectiveError.UNSUPPORTED);
+                log.warn("Unsupported next objective type {}", nextObjective.type());
+                break;
+            default:
+                fail(nextObjective, ObjectiveError.UNKNOWN);
+                log.warn("Unknown next objective type {}", nextObjective.type());
         }
     }
 
@@ -243,7 +249,7 @@
             p = (PortCriterion) filt.key();
         } else {
             log.warn("No key defined in filtering objective from app: {}. Not"
-                    + "processing filtering objective", applicationId);
+                             + "processing filtering objective", applicationId);
             fail(filt, ObjectiveError.UNKNOWN);
             return;
         }
@@ -261,6 +267,7 @@
                 fail(filt, ObjectiveError.UNSUPPORTED);
                 return;
             }
+
         }
 
         log.debug("Modifying Port/VLAN/MAC filtering rules in filter table: {}/{}/{}",
@@ -276,7 +283,7 @@
             selector.matchEthDst(e.mac());
         }
         selector.matchVlanId(v.vlanId());
-        selector.matchEthType(Ethernet.TYPE_IPV4);
+        selector.matchEthType(TYPE_IPV4);
         if (!v.vlanId().equals(VlanId.NONE)) {
             treatment.popVlan();
         }
@@ -291,6 +298,29 @@
                 .forTable(FILTER_TABLE).build();
 
         ops = install ? ops.add(rule) : ops.remove(rule);
+        // FIXME IPv6 in the multicast use case.
+        // Filtering rules for IPv6.
+        if (e.mask() == null) {
+            selector = DefaultTrafficSelector.builder();
+            selector.matchInPort(p.port());
+            selector.matchEthDst(e.mac());
+            selector.matchVlanId(v.vlanId());
+            selector.matchEthType(Ethernet.TYPE_IPV6);
+            if (!v.vlanId().equals(VlanId.NONE)) {
+                treatment.popVlan();
+            }
+            treatment.transition(FIB_TABLE);
+            rule = DefaultFlowRule.builder()
+                    .forDevice(deviceId)
+                    .withSelector(selector.build())
+                    .withTreatment(treatment.build())
+                    .withPriority(DEFAULT_PRIORITY)
+                    .fromApp(applicationId)
+                    .makePermanent()
+                    .forTable(FILTER_TABLE).build();
+
+            ops = install ? ops.add(rule) : ops.remove(rule);
+        }
         // apply filtering flow rules
         flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
             @Override
@@ -309,13 +339,13 @@
 
     private Collection<FlowRule> processForward(ForwardingObjective fwd) {
         switch (fwd.flag()) {
-        case SPECIFIC:
-            return processSpecific(fwd);
-        case VERSATILE:
-            return processVersatile(fwd);
-        default:
-            fail(fwd, ObjectiveError.UNKNOWN);
-            log.warn("Unknown forwarding flag {}", fwd.flag());
+            case SPECIFIC:
+                return processSpecific(fwd);
+            case VERSATILE:
+                return processVersatile(fwd);
+            default:
+                fail(fwd, ObjectiveError.UNKNOWN);
+                log.warn("Unknown forwarding flag {}", fwd.flag());
         }
         return Collections.emptySet();
     }
@@ -335,7 +365,7 @@
         Collection<FlowRule> flowrules = new ArrayList<>();
         if (fwd.nextId() == null && fwd.treatment() == null) {
             log.error("Forwarding objective {} from {} must contain "
-                    + "nextId or Treatment", fwd.selector(), fwd.appId());
+                              + "nextId or Treatment", fwd.selector(), fwd.appId());
             return Collections.emptySet();
         }
 
@@ -344,7 +374,8 @@
         // A punt rule for IP traffic should be directed to the FIB table
         // so that it only takes effect if the packet misses the FIB rules
         if (fwd.treatment() != null && containsPunt(fwd.treatment()) &&
-                fwd.selector() != null && matchesIp(fwd.selector())) {
+                fwd.selector() != null && matchesIp(fwd.selector()) &&
+                !matchesControlTraffic(fwd.selector())) {
             tableId = FIB_TABLE;
         }
 
@@ -395,7 +426,24 @@
 
     private boolean matchesIp(TrafficSelector selector) {
         EthTypeCriterion c = (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
-        return c != null && c.ethType().equals(EthType.EtherType.IPV4.ethType());
+        return c != null && (c.ethType().equals(EthType.EtherType.IPV4.ethType()) ||
+                        c.ethType().equals(EthType.EtherType.IPV6.ethType()));
+    }
+
+    private boolean matchesControlTraffic(TrafficSelector selector) {
+        EthTypeCriterion c = (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
+        if (c != null && c.ethType().equals(EthType.EtherType.ARP.ethType())) {
+            return true;
+        } else if (c != null && c.ethType().equals(EthType.EtherType.IPV6.ethType())) {
+            IPProtocolCriterion i = (IPProtocolCriterion) selector.getCriterion(Criterion.Type.IP_PROTO);
+            if (i != null && i.protocol() == PROTOCOL_ICMP6) {
+                Icmpv6TypeCriterion ic = (Icmpv6TypeCriterion) selector.getCriterion(Criterion.Type.ICMPV6_TYPE);
+                if (ic.icmpv6Type() != ECHO_REQUEST && ic.icmpv6Type() != ECHO_REPLY) {
+                    return true;
+                }
+            }
+        }
+        return false;
     }
 
     /**
@@ -415,19 +463,34 @@
         EthTypeCriterion ethType =
                 (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
         // XXX currently supporting only the L3 unicast table
-        if (ethType == null || ethType.ethType().toShort() != Ethernet.TYPE_IPV4) {
+        if (ethType == null || (ethType.ethType().toShort() != TYPE_IPV4
+                && ethType.ethType().toShort() != Ethernet.TYPE_IPV6)) {
             fail(fwd, ObjectiveError.UNSUPPORTED);
             return Collections.emptySet();
         }
+        //We build the selector according the eth type.
+        IpPrefix ipPrefix;
+        TrafficSelector.Builder filteredSelector;
+        if (ethType.ethType().toShort() == TYPE_IPV4) {
+            ipPrefix = ((IPCriterion)
+                    selector.getCriterion(Criterion.Type.IPV4_DST)).ip();
 
-        IpPrefix ipPrefix = ((IPCriterion)
-                selector.getCriterion(Criterion.Type.IPV4_DST)).ip();
+            filteredSelector = DefaultTrafficSelector.builder()
+                    .matchEthType(TYPE_IPV4);
+        } else {
+            ipPrefix = ((IPCriterion)
+                    selector.getCriterion(Criterion.Type.IPV6_DST)).ip();
 
-        TrafficSelector.Builder filteredSelector = DefaultTrafficSelector.builder()
-                        .matchEthType(Ethernet.TYPE_IPV4);
-
+            filteredSelector = DefaultTrafficSelector.builder()
+                    .matchEthType(Ethernet.TYPE_IPV6);
+        }
+        // If the prefix is different from the default via.
         if (ipPrefix.prefixLength() != 0) {
-            filteredSelector.matchIPDst(ipPrefix);
+            if (ethType.ethType().toShort() == TYPE_IPV4) {
+                filteredSelector.matchIPDst(ipPrefix);
+            } else {
+                filteredSelector.matchIPv6Dst(ipPrefix);
+            }
         }
 
         TrafficTreatment tt = null;