ONOS-509: Match IPV6_SRC, IPV6_DST criteria

* Following classes have been extended to match IPV6_SRC and IPV6_DST criteria:
    - Criteria
    - DefaultTrafficSelector
    - FlowEntryBuilder
    - FlowModBuilder

* Not included in this submission
    - Match of other IPv6-related criteria
    - Set of all IPv6-related criteria (action)

Change-Id: I115829a480356d2037e0fc809595f7a5eec25874
diff --git a/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficSelector.java b/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficSelector.java
index 1caed8e..756fdd7 100644
--- a/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficSelector.java
+++ b/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficSelector.java
@@ -184,6 +184,16 @@
         }
 
         @Override
+        public Builder matchIPv6Src(IpPrefix ip) {
+            return add(Criteria.matchIPv6Src(ip));
+        }
+
+        @Override
+        public Builder matchIPv6Dst(IpPrefix ip) {
+            return add(Criteria.matchIPv6Dst(ip));
+        }
+
+        @Override
         public Builder matchMplsLabel(Integer mplsLabel) {
             return add(Criteria.matchMplsLabel(mplsLabel));
         }
diff --git a/core/api/src/main/java/org/onosproject/net/flow/TrafficSelector.java b/core/api/src/main/java/org/onosproject/net/flow/TrafficSelector.java
index d9b91f6..267fb51 100644
--- a/core/api/src/main/java/org/onosproject/net/flow/TrafficSelector.java
+++ b/core/api/src/main/java/org/onosproject/net/flow/TrafficSelector.java
@@ -115,7 +115,7 @@
         public Builder matchIPProtocol(Byte proto);
 
         /**
-         * Matches a l3 address.
+         * Matches a l3 IPv4 address.
          *
          * @param ip a l3 address
          * @return a selection builder
@@ -123,7 +123,7 @@
         public Builder matchIPSrc(IpPrefix ip);
 
         /**
-         * Matches a l3 address.
+         * Matches a l3 IPv4 address.
          *
          * @param ip a l3 address
          * @return a selection builder
@@ -146,6 +146,21 @@
          */
         public Builder matchTcpDst(Short tcpPort);
 
+        /**
+         * Matches a l3 IPv6 address.
+         *
+         * @param ip a l3 IPv6 address
+         * @return a selection builder
+         */
+        public Builder matchIPv6Src(IpPrefix ip);
+
+        /**
+         * Matches a l3 IPv6 address.
+         *
+         * @param ip a l3 IPv6 address
+         * @return a selection builder
+         */
+        public Builder matchIPv6Dst(IpPrefix ip);
 
         /**
          * Matches on a MPLS label .
diff --git a/core/api/src/main/java/org/onosproject/net/flow/criteria/Criteria.java b/core/api/src/main/java/org/onosproject/net/flow/criteria/Criteria.java
index dcc7edc..e725143 100644
--- a/core/api/src/main/java/org/onosproject/net/flow/criteria/Criteria.java
+++ b/core/api/src/main/java/org/onosproject/net/flow/criteria/Criteria.java
@@ -18,7 +18,6 @@
 import static com.google.common.base.MoreObjects.toStringHelper;
 
 import java.util.Objects;
-
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.flow.criteria.Criterion.Type;
 import org.onlab.packet.IpPrefix;
@@ -109,9 +108,9 @@
     }
 
     /**
-     * Creates a match on IP source field using the specified value.
+     * Creates a match on IPv4 source field using the specified value.
      *
-     * @param ip ip source value
+     * @param ip ipv4 source value
      * @return match criterion
      */
     public static Criterion matchIPSrc(IpPrefix ip) {
@@ -119,9 +118,9 @@
     }
 
     /**
-     * Creates a match on IP destination field using the specified value.
+     * Creates a match on IPv4 destination field using the specified value.
      *
-     * @param ip ip source value
+     * @param ip ipv4 source value
      * @return match criterion
      */
     public static Criterion matchIPDst(IpPrefix ip) {
@@ -149,6 +148,26 @@
     }
 
     /**
+     * Creates a match on IPv6 source field using the specified value.
+     *
+     * @param ip ipv6 source value
+     * @return match criterion
+     */
+    public static Criterion matchIPv6Src(IpPrefix ip) {
+        return new IPCriterion(ip, Type.IPV6_SRC);
+    }
+
+    /**
+     * Creates a match on IPv6 destination field using the specified value.
+     *
+     * @param ip ipv6 source value
+     * @return match criterion
+     */
+    public static Criterion matchIPv6Dst(IpPrefix ip) {
+        return new IPCriterion(ip, Type.IPV6_DST);
+    }
+
+    /**
      * Creates a match on MPLS label.
      * @param mplsLabel MPLS label
      * @return match criterion
diff --git a/core/api/src/test/java/org/onosproject/net/flow/DefaultTrafficSelectorTest.java b/core/api/src/test/java/org/onosproject/net/flow/DefaultTrafficSelectorTest.java
index a1ce3d9..651773f 100644
--- a/core/api/src/test/java/org/onosproject/net/flow/DefaultTrafficSelectorTest.java
+++ b/core/api/src/test/java/org/onosproject/net/flow/DefaultTrafficSelectorTest.java
@@ -129,6 +129,7 @@
         final short shortValue = 33;
         final byte byteValue = 44;
         final IpPrefix ipPrefixValue = IpPrefix.valueOf("192.168.1.0/24");
+        final IpPrefix ipv6PrefixValue = IpPrefix.valueOf("fe80::1/64");
 
         selector = DefaultTrafficSelector.builder()
                 .matchInport(PortNumber.portNumber(11)).build();
@@ -175,6 +176,14 @@
         assertThat(selector, hasCriterionWithType(Type.TCP_DST));
 
         selector = DefaultTrafficSelector.builder()
+                .matchIPv6Src(ipv6PrefixValue).build();
+        assertThat(selector, hasCriterionWithType(Type.IPV6_SRC));
+
+        selector = DefaultTrafficSelector.builder()
+                .matchIPv6Dst(ipv6PrefixValue).build();
+        assertThat(selector, hasCriterionWithType(Type.IPV6_DST));
+
+        selector = DefaultTrafficSelector.builder()
                 .matchMplsLabel(3).build();
         assertThat(selector, hasCriterionWithType(Type.MPLS_LABEL));
 
diff --git a/core/api/src/test/java/org/onosproject/net/flow/criteria/CriteriaTest.java b/core/api/src/test/java/org/onosproject/net/flow/criteria/CriteriaTest.java
index 0fc5d30..a11b227 100644
--- a/core/api/src/test/java/org/onosproject/net/flow/criteria/CriteriaTest.java
+++ b/core/api/src/test/java/org/onosproject/net/flow/criteria/CriteriaTest.java
@@ -84,11 +84,18 @@
 
     private static final String IP1 = "1.2.3.4/24";
     private static final String IP2 = "5.6.7.8/24";
+    private static final String IPV61 = "fe80::1/64";
+    private static final String IPV62 = "fc80::2/64";
     private IpPrefix ip1 = IpPrefix.valueOf(IP1);
     private IpPrefix ip2 = IpPrefix.valueOf(IP2);
+    private IpPrefix ipv61 = IpPrefix.valueOf(IPV61);
+    private IpPrefix ipv62 = IpPrefix.valueOf(IPV62);
     Criterion matchIp1 = Criteria.matchIPSrc(ip1);
     Criterion sameAsMatchIp1 = Criteria.matchIPSrc(ip1);
     Criterion matchIp2 = Criteria.matchIPSrc(ip2);
+    Criterion matchIpv61 = Criteria.matchIPSrc(ipv61);
+    Criterion sameAsMatchIpv61 = Criteria.matchIPSrc(ipv61);
+    Criterion matchIpv62 = Criteria.matchIPSrc(ipv62);
 
     short lambda1 = 1;
     short lambda2 = 2;
@@ -384,6 +391,32 @@
     }
 
     /**
+     * Test the matchIPSrc method.
+     */
+    @Test
+    public void testMatchIPv6SrcMethod() {
+        Criterion matchIpv6Src = Criteria.matchIPv6Src(ipv61);
+        Criteria.IPCriterion ipCriterion =
+                checkAndConvert(matchIpv6Src,
+                        Criterion.Type.IPV6_SRC,
+                        Criteria.IPCriterion.class);
+        assertThat(ipCriterion.ip(), is(ipv61));
+    }
+
+    /**
+     * Test the matchIPDst method.
+     */
+    @Test
+    public void testMatchIPv6DstMethod() {
+        Criterion matchIPv6Dst = Criteria.matchIPv6Dst(ipv61);
+        Criteria.IPCriterion ipCriterion =
+                checkAndConvert(matchIPv6Dst,
+                        Criterion.Type.IPV6_DST,
+                        Criteria.IPCriterion.class);
+        assertThat(ipCriterion.ip(), is(equalTo(ipv61)));
+    }
+
+    /**
      * Test the equals() method of the IpCriterion class.
      */
     @Test
@@ -392,6 +425,11 @@
                 .addEqualityGroup(matchIp1, sameAsMatchIp1)
                 .addEqualityGroup(matchIp2)
                 .testEquals();
+
+        new EqualsTester()
+                .addEqualityGroup(matchIpv61, sameAsMatchIpv61)
+                .addEqualityGroup(matchIpv62)
+                .testEquals();
     }
 
     // LambdaCriterion class
diff --git a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java
index e6060a2..3ea2efa 100644
--- a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java
+++ b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java
@@ -21,6 +21,7 @@
 
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.Ip4Prefix;
+import org.onlab.packet.Ip6Prefix;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
 import org.onosproject.net.DeviceId;
@@ -56,6 +57,7 @@
 import org.projectfloodlight.openflow.protocol.oxm.OFOxm;
 import org.projectfloodlight.openflow.protocol.oxm.OFOxmOchSigidBasic;
 import org.projectfloodlight.openflow.types.IPv4Address;
+import org.projectfloodlight.openflow.types.IPv6Address;
 import org.projectfloodlight.openflow.types.Masked;
 import org.projectfloodlight.openflow.types.OFVlanVidMatch;
 import org.projectfloodlight.openflow.types.VlanPcp;
@@ -398,6 +400,34 @@
                 builder.matchOpticalSignalType(match.get(MatchField
                                                                  .OCH_SIGTYPE).getValue());
                 break;
+            case IPV6_DST:
+                Ip6Prefix dipv6;
+                if (match.isPartiallyMasked(MatchField.IPV6_DST)) {
+                    Masked<IPv6Address> maskedIp = match.getMasked(MatchField.IPV6_DST);
+                    dipv6 = Ip6Prefix.valueOf(
+                            maskedIp.getValue().getBytes(),
+                            maskedIp.getMask().asCidrMaskLength());
+                } else {
+                    dipv6 = Ip6Prefix.valueOf(
+                            match.get(MatchField.IPV6_DST).getBytes(),
+                            Ip6Prefix.MAX_MASK_LENGTH);
+                }
+                builder.matchIPv6Dst(dipv6);
+                break;
+            case IPV6_SRC:
+                Ip6Prefix sipv6;
+                if (match.isPartiallyMasked(MatchField.IPV6_SRC)) {
+                    Masked<IPv6Address> maskedIp = match.getMasked(MatchField.IPV6_SRC);
+                    sipv6 = Ip6Prefix.valueOf(
+                            maskedIp.getValue().getBytes(),
+                            maskedIp.getMask().asCidrMaskLength());
+                } else {
+                    sipv6 = Ip6Prefix.valueOf(
+                            match.get(MatchField.IPV6_SRC).getBytes(),
+                            Ip6Prefix.MAX_MASK_LENGTH);
+                }
+                builder.matchIPv6Src(sipv6);
+                break;
             case ARP_OP:
             case ARP_SHA:
             case ARP_SPA:
@@ -408,12 +438,10 @@
             case ICMPV6_CODE:
             case ICMPV6_TYPE:
             case IN_PHY_PORT:
-            case IPV6_DST:
             case IPV6_FLABEL:
             case IPV6_ND_SLL:
             case IPV6_ND_TARGET:
             case IPV6_ND_TLL:
-            case IPV6_SRC:
             case IP_DSCP:
             case IP_ECN:
             case METADATA:
@@ -425,8 +453,6 @@
             case UDP_SRC:
             default:
                 log.warn("Match type {} not yet implemented.", field.id);
-
-
             }
         }
         return builder.build();
diff --git a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilder.java b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilder.java
index 9f483c3..58a5720 100644
--- a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilder.java
+++ b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilder.java
@@ -17,6 +17,8 @@
 
 import static org.slf4j.LoggerFactory.getLogger;
 
+import org.onlab.packet.Ip6Address;
+import org.onlab.packet.Ip6Prefix;
 import org.onosproject.net.flow.FlowRule;
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.criteria.Criteria;
@@ -41,6 +43,7 @@
 import org.projectfloodlight.openflow.types.CircuitSignalID;
 import org.projectfloodlight.openflow.types.EthType;
 import org.projectfloodlight.openflow.types.IPv4Address;
+import org.projectfloodlight.openflow.types.IPv6Address;
 import org.projectfloodlight.openflow.types.IpProtocol;
 import org.projectfloodlight.openflow.types.MacAddress;
 import org.projectfloodlight.openflow.types.Masked;
@@ -134,6 +137,7 @@
         EthCriterion eth;
         IPCriterion ip;
         Ip4Prefix ip4Prefix;
+        Ip6Prefix ip6Prefix;
         TcpPortCriterion tp;
         for (Criterion c : selector.criteria()) {
             switch (c.type()) {
@@ -220,6 +224,36 @@
                 mBuilder.setExact(MatchField.OCH_SIGTYPE,
                                   U8.of(sc.signalType()));
                 break;
+            case IPV6_DST:
+                ip = (IPCriterion) c;
+                ip6Prefix = ip.ip().getIp6Prefix();
+                if (ip6Prefix.prefixLength() != Ip6Prefix.MAX_MASK_LENGTH) {
+                    Ip6Address maskAddr =
+                            Ip6Address.makeMaskPrefix(ip6Prefix.prefixLength());
+                    Masked<IPv6Address> maskedIp =
+                            Masked.of(IPv6Address.of(ip6Prefix.address().toString()),
+                                    IPv6Address.of(maskAddr.toString()));
+                    mBuilder.setMasked(MatchField.IPV6_DST, maskedIp);
+                } else {
+                    mBuilder.setExact(MatchField.IPV6_DST,
+                            IPv6Address.of(ip6Prefix.address().toString()));
+                }
+                break;
+            case IPV6_SRC:
+                ip = (IPCriterion) c;
+                ip6Prefix = ip.ip().getIp6Prefix();
+                if (ip6Prefix.prefixLength() != Ip6Prefix.MAX_MASK_LENGTH) {
+                    Ip6Address maskAddr =
+                            Ip6Address.makeMaskPrefix(ip6Prefix.prefixLength());
+                    Masked<IPv6Address> maskedIp =
+                            Masked.of(IPv6Address.of(ip6Prefix.address().toString()),
+                                    IPv6Address.of(maskAddr.toString()));
+                    mBuilder.setMasked(MatchField.IPV6_SRC, maskedIp);
+                } else {
+                    mBuilder.setExact(MatchField.IPV6_SRC,
+                            IPv6Address.of(ip6Prefix.address().toString()));
+                }
+                break;
             case ARP_OP:
             case ARP_SHA:
             case ARP_SPA:
@@ -230,13 +264,11 @@
             case ICMPV6_CODE:
             case ICMPV6_TYPE:
             case IN_PHY_PORT:
-            case IPV6_DST:
             case IPV6_EXTHDR:
             case IPV6_FLABEL:
             case IPV6_ND_SLL:
             case IPV6_ND_TARGET:
             case IPV6_ND_TLL:
-            case IPV6_SRC:
             case IP_DSCP:
             case IP_ECN:
             case METADATA: