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/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;