[ONOS-7653] ACL app enhancements for including more matching criteria in rules

Change-Id: Ic29bab0c0752d6280a5feff992a69a2e10084414
diff --git a/apps/acl/src/main/java/org/onosproject/acl/AclRule.java b/apps/acl/src/main/java/org/onosproject/acl/AclRule.java
index 02b7025..8587d7c 100644
--- a/apps/acl/src/main/java/org/onosproject/acl/AclRule.java
+++ b/apps/acl/src/main/java/org/onosproject/acl/AclRule.java
@@ -23,6 +23,7 @@
 import com.google.common.base.MoreObjects;
 import org.onlab.packet.IPv4;
 import org.onlab.packet.Ip4Prefix;
+import org.onlab.packet.MacAddress;
 import org.onosproject.core.IdGenerator;
 
 import java.util.Objects;
@@ -37,10 +38,14 @@
 
     private final RuleId id;
 
+    private final MacAddress srcMac;
+    private final MacAddress dstMac;
     private final Ip4Prefix srcIp;
     private final Ip4Prefix dstIp;
     private final byte ipProto;
+    private final short srcTpPort;
     private final short dstTpPort;
+    private final byte dscp;
     private final Action action;
 
     protected static IdGenerator idGenerator;
@@ -58,10 +63,14 @@
      */
     private AclRule() {
         this.id = null;
+        this.srcMac = null;
+        this.dstMac = null;
         this.srcIp = null;
         this.dstIp = null;
+        this.dscp = 0;
         this.ipProto = 0;
         this.dstTpPort = 0;
+        this.srcTpPort = 0;
         this.action = null;
     }
 
@@ -69,21 +78,29 @@
      * Create a new ACL rule.
      *
      * @param srcIp     source IP address
+     * @param srcMac    source Mac address
+     * @param dstMac    destination Mac address
      * @param dstIp     destination IP address
      * @param ipProto   IP protocol
+     * @param dscp      IP dscp
      * @param dstTpPort destination transport layer port
+     * @param srcTpPort source transport layer port
      * @param action    ACL rule's action
      */
-    private AclRule(Ip4Prefix srcIp, Ip4Prefix dstIp, byte ipProto,
-                    short dstTpPort, Action action) {
+    private AclRule(MacAddress srcMac, MacAddress dstMac, Ip4Prefix srcIp, Ip4Prefix dstIp, byte ipProto,
+                    byte dscp, short dstTpPort, short srcTpPort, Action action) {
         synchronized (ID_GENERATOR_LOCK) {
             checkState(idGenerator != null, "Id generator is not bound.");
             this.id = RuleId.valueOf(idGenerator.getNewId());
         }
+        this.srcMac = srcMac;
+        this.dstMac = dstMac;
         this.srcIp = srcIp;
         this.dstIp = dstIp;
         this.ipProto = ipProto;
+        this.dscp = dscp;
         this.dstTpPort = dstTpPort;
+        this.srcTpPort = srcTpPort;
         this.action = action;
     }
 
@@ -119,7 +136,11 @@
      */
     public boolean checkMatch(AclRule r) {
         return (this.dstTpPort == r.dstTpPort || r.dstTpPort == 0)
+                && (this.srcTpPort == r.srcTpPort || r.srcTpPort == 0)
                 && (this.ipProto == r.ipProto || r.ipProto == 0)
+                && (this.dscp == r.dscp || r.dscp == 0)
+                && (this.srcMac == r.srcMac || r.srcMac == null)
+                && (this.dstMac == r.dstMac || r.dstMac == null)
                 && (checkCidrInCidr(this.srcIp(), r.srcIp()))
                 && (checkCidrInCidr(this.dstIp(), r.dstIp()));
     }
@@ -140,8 +161,12 @@
 
         private Ip4Prefix srcIp = null;
         private Ip4Prefix dstIp = null;
+        private MacAddress srcMac = null;
+        private MacAddress dstMac = null;
         private byte ipProto = 0;
+        private byte dscp = 0;
         private short dstTpPort = 0;
+        private short srcTpPort = 0;
         private Action action = Action.DENY;
 
         private Builder() {
@@ -149,6 +174,28 @@
         }
 
         /**
+         * Sets the source Mac address for the ACL rule that will be built.
+         *
+         * @param srcMac source Mac address to use for built ACL rule
+         * @return this builder
+         */
+        public Builder srcMac(MacAddress srcMac) {
+            this.srcMac = srcMac;
+            return this;
+        }
+
+        /**
+         * Sets the destination Mac address for the ACL rule that will be built.
+         *
+         * @param dstMac destination Mac address to use for built ACL rule
+         * @return this builder
+         */
+        public Builder dstMac(MacAddress dstMac) {
+            this.dstMac = dstMac;
+            return this;
+        }
+
+        /**
          * Sets the source IP address for the ACL rule that will be built.
          *
          * @param srcIp source IP address to use for built ACL rule
@@ -181,6 +228,18 @@
             return this;
         }
 
+
+        /**
+         * Sets the IP dscp for the ACL rule that will be built.
+         *
+         * @param dscp IP dscp to use for built ACL rule
+         * @return this builder
+         */
+        public Builder dscp(byte dscp) {
+            this.dscp = dscp;
+            return this;
+        }
+
         /**
          * Sets the destination transport layer port for the ACL rule that will be built.
          *
@@ -195,6 +254,19 @@
         }
 
         /**
+         * Sets the source transport layer port for the ACL rule that will be built.
+         *
+         * @param srcTpPort destination transport layer port to use for built ACL rule
+         * @return this builder
+         */
+        public Builder srcTpPort(short srcTpPort) {
+            if ((ipProto == IPv4.PROTOCOL_TCP || ipProto == IPv4.PROTOCOL_UDP)) {
+                this.srcTpPort = srcTpPort;
+            }
+            return this;
+        }
+
+        /**
          * Sets the action for the ACL rule that will be built.
          *
          * @param action action to use for built ACL rule
@@ -215,7 +287,7 @@
             checkState(ipProto == 0 || ipProto == IPv4.PROTOCOL_ICMP
                                || ipProto == IPv4.PROTOCOL_TCP || ipProto == IPv4.PROTOCOL_UDP,
                        "ipProto must be assigned to TCP, UDP, or ICMP.");
-            return new AclRule(srcIp, dstIp, ipProto, dstTpPort, action);
+            return new AclRule(srcMac, dstMac, srcIp, dstIp, ipProto, dscp, dstTpPort, srcTpPort, action);
         }
 
     }
@@ -238,6 +310,14 @@
         return id;
     }
 
+    public MacAddress srcMac() {
+        return srcMac;
+    }
+
+    public MacAddress dstMac() {
+        return dstMac;
+    }
+
     public Ip4Prefix srcIp() {
         return srcIp;
     }
@@ -250,17 +330,26 @@
         return ipProto;
     }
 
+    public byte dscp() {
+        return dscp;
+    }
+
     public short dstTpPort() {
         return dstTpPort;
     }
 
+    public short srcTpPort() {
+        return srcTpPort;
+    }
+
     public Action action() {
         return action;
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(action, id.fingerprint(), ipProto, srcIp, dstIp, dstTpPort);
+        return Objects.hash(action, id.fingerprint(), srcMac, dstMac, ipProto, dscp,
+                srcIp, dstIp, dstTpPort, srcTpPort);
     }
 
     @Override
@@ -271,10 +360,14 @@
         if (obj instanceof AclRule) {
             AclRule that = (AclRule) obj;
             return Objects.equals(id, that.id) &&
+                    Objects.equals(srcMac, that.srcMac) &&
+                    Objects.equals(dstMac, that.dstMac) &&
                     Objects.equals(srcIp, that.srcIp) &&
                     Objects.equals(dstIp, that.dstIp) &&
                     Objects.equals(ipProto, that.ipProto) &&
+                    Objects.equals(dscp, that.dscp) &&
                     Objects.equals(dstTpPort, that.dstTpPort) &&
+                    Objects.equals(srcTpPort, that.srcTpPort) &&
                     Objects.equals(action, that.action);
         }
         return false;
@@ -285,10 +378,14 @@
         return MoreObjects.toStringHelper(this)
                 .omitNullValues()
                 .add("id", id)
+                .add("srcMac", srcMac)
+                .add("dstMac", dstMac)
                 .add("srcIp", srcIp)
                 .add("dstIp", dstIp)
                 .add("ipProto", ipProto)
+                .add("dscp", dscp)
                 .add("dstTpPort", dstTpPort)
+                .add("srcTpPort", srcTpPort)
                 .add("action", action)
                 .toString();
     }
diff --git a/apps/acl/src/main/java/org/onosproject/acl/AclWebResource.java b/apps/acl/src/main/java/org/onosproject/acl/AclWebResource.java
index 0cda20b..74b572c 100644
--- a/apps/acl/src/main/java/org/onosproject/acl/AclWebResource.java
+++ b/apps/acl/src/main/java/org/onosproject/acl/AclWebResource.java
@@ -26,6 +26,7 @@
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import org.onlab.packet.IPv4;
 import org.onlab.packet.Ip4Prefix;
+import org.onlab.packet.MacAddress;
 import org.onosproject.rest.AbstractWebResource;
 
 import javax.ws.rs.Consumes;
@@ -65,12 +66,21 @@
         for (AclRule rule : rules) {
             ObjectNode node = mapper.createObjectNode();
             node.put("id", rule.id().toString());
+            if (rule.srcMac() != null) {
+                node.put("srcMac", rule.srcMac().toString());
+            }
+            if (rule.dstMac() != null) {
+                node.put("dstMac", rule.dstMac().toString());
+            }
             if (rule.srcIp() != null) {
                 node.put("srcIp", rule.srcIp().toString());
             }
             if (rule.dstIp() != null) {
                 node.put("dstIp", rule.dstIp().toString());
             }
+            if (rule.dscp() != 0) {
+                node.put("dscp", rule.dscp());
+            }
             if (rule.ipProto() != 0) {
                 switch (rule.ipProto()) {
                     case IPv4.PROTOCOL_ICMP:
@@ -89,6 +99,9 @@
             if (rule.dstTpPort() != 0) {
                 node.put("dstTpPort", rule.dstTpPort());
             }
+            if (rule.srcTpPort() != 0) {
+                node.put("srcTpPort", rule.srcTpPort());
+            }
             node.put("action", rule.action().toString());
             arrayNode.add(node);
         }
@@ -160,6 +173,21 @@
             rule.dstIp(Ip4Prefix.valueOf(s));
         }
 
+        s = node.path("srcMac").asText(null);
+        if (s != null) {
+            rule.srcMac(MacAddress.valueOf(s));
+        }
+
+        s = node.path("dstMac").asText(null);
+        if (s != null) {
+            rule.dstMac(MacAddress.valueOf(s));
+        }
+
+        s = node.path("dscp").asText(null);
+        if (s != null) {
+            rule.dscp(Byte.valueOf(s));
+        }
+
         s = node.path("ipProto").asText(null);
         if (s != null) {
             if ("TCP".equalsIgnoreCase(s)) {
@@ -178,6 +206,11 @@
             rule.dstTpPort((short) port);
         }
 
+        port = node.path("srcTpPort").asInt(0);
+        if (port > 0) {
+            rule.srcTpPort((short) port);
+        }
+
         s = node.path("action").asText(null);
         if (s != null) {
             if ("allow".equalsIgnoreCase(s)) {
diff --git a/apps/acl/src/main/java/org/onosproject/acl/impl/AclManager.java b/apps/acl/src/main/java/org/onosproject/acl/impl/AclManager.java
index 2c735f2..3b8617e 100644
--- a/apps/acl/src/main/java/org/onosproject/acl/impl/AclManager.java
+++ b/apps/acl/src/main/java/org/onosproject/acl/impl/AclManager.java
@@ -265,6 +265,14 @@
         TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
         FlowEntry.Builder flowEntry = DefaultFlowEntry.builder();
 
+        if (rule.srcMac() != null) {
+            selectorBuilder.matchEthSrc(rule.srcMac());
+
+        }
+        if (rule.dstMac() != null) {
+            selectorBuilder.matchEthDst(rule.dstMac());
+        }
+
         selectorBuilder.matchEthType(Ethernet.TYPE_IPV4);
         if (rule.srcIp() != null) {
             selectorBuilder.matchIPSrc(rule.srcIp());
@@ -277,6 +285,9 @@
         if (rule.ipProto() != 0) {
             selectorBuilder.matchIPProtocol(Integer.valueOf(rule.ipProto()).byteValue());
         }
+        if (rule.dscp() != 0) {
+            selectorBuilder.matchIPDscp(Byte.valueOf(rule.dscp()));
+        }
         if (rule.dstTpPort() != 0) {
             switch (rule.ipProto()) {
                 case IPv4.PROTOCOL_TCP:
@@ -289,6 +300,20 @@
                     break;
             }
         }
+
+        if (rule.srcTpPort() != 0) {
+            switch (rule.ipProto()) {
+                case IPv4.PROTOCOL_TCP:
+                    selectorBuilder.matchTcpSrc(TpPort.tpPort(rule.srcTpPort()));
+                    break;
+                case IPv4.PROTOCOL_UDP:
+                    selectorBuilder.matchUdpSrc(TpPort.tpPort(rule.srcTpPort()));
+                    break;
+                default:
+                    break;
+            }
+        }
+
         if (rule.action() == AclRule.Action.ALLOW) {
             treatment.add(Instructions.createOutput(PortNumber.CONTROLLER));
         }