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 @@
         }
     }
 }
+