ONOS-2997 Cleaned up the package name-space and the REST API of the ACL app; REST API needs more work and should use codecs.

Change-Id: Ibb52740befb99185f9495b54994903fadf9f79bc
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
new file mode 100644
index 0000000..f5c0c20
--- /dev/null
+++ b/apps/acl/src/main/java/org/onosproject/acl/impl/AclManager.java
@@ -0,0 +1,338 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ * Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China
+ * Advisers: Keqiu Li, Heng Qi and Haisheng Yu
+ * This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002)
+ * and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.acl.impl;
+
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip4Prefix;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.TpPort;
+import org.onosproject.acl.AclRule;
+import org.onosproject.acl.AclService;
+import org.onosproject.acl.AclStore;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onosproject.acl.RuleId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.core.IdGenerator;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Host;
+import org.onosproject.net.MastershipRole;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.DefaultFlowEntry;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.host.HostEvent;
+import org.onosproject.net.host.HostListener;
+import org.onosproject.net.host.HostService;
+import org.slf4j.Logger;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Implementation of the ACL service.
+ */
+@Component(immediate = true)
+@Service
+public class AclManager implements AclService {
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected FlowRuleService flowRuleService;
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected HostService hostService;
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected MastershipService mastershipService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected AclStore aclStore;
+
+    private final Logger log = getLogger(getClass());
+    private ApplicationId appId;
+    private final HostListener hostListener = new InternalHostListener();
+    private IdGenerator idGenerator;
+
+    /**
+     * Checks if the given IP address is in the given CIDR address.
+     */
+    private boolean checkIpInCIDR(Ip4Address ip, Ip4Prefix cidr) {
+        int offset = 32 - cidr.prefixLength();
+        int cidrPrefix = cidr.address().toInt();
+        int ipIntValue = ip.toInt();
+        cidrPrefix = cidrPrefix >> offset;
+        ipIntValue = ipIntValue >> offset;
+        cidrPrefix = cidrPrefix << offset;
+        ipIntValue = ipIntValue << offset;
+
+        return (cidrPrefix == ipIntValue);
+    }
+
+    private class InternalHostListener implements HostListener {
+
+        /**
+         * Generate new ACL flow rules for new host following the given ACL rule.
+         */
+        private void processHostAddedEvent(HostEvent event, AclRule rule) {
+            DeviceId deviceId = event.subject().location().deviceId();
+            for (IpAddress address : event.subject().ipAddresses()) {
+                if ((rule.srcIp() != null) ?
+                        (checkIpInCIDR(address.getIp4Address(), rule.srcIp())) :
+                        (checkIpInCIDR(address.getIp4Address(), rule.dstIp()))) {
+                    if (!aclStore.checkIfRuleWorksInDevice(rule.id(), deviceId)) {
+                        List<RuleId> allowingRuleList = aclStore
+                                .getAllowingRuleByDenyingRule(rule.id());
+                        if (allowingRuleList != null) {
+                            for (RuleId allowingRuleId : allowingRuleList) {
+                                generateACLFlow(aclStore.getAclRule(allowingRuleId), deviceId);
+                            }
+                        }
+                        generateACLFlow(rule, deviceId);
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void event(HostEvent event) {
+            // if a new host appears and an existing rule denies
+            // its traffic, a new ACL flow rule is generated.
+            if (event.type() == HostEvent.Type.HOST_ADDED) {
+                DeviceId deviceId = event.subject().location().deviceId();
+                if (mastershipService.getLocalRole(deviceId) == MastershipRole.MASTER) {
+                    for (AclRule rule : aclStore.getAclRules()) {
+                        if (rule.action() != AclRule.Action.ALLOW) {
+                            processHostAddedEvent(event, rule);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    @Activate
+    public void activate() {
+        appId = coreService.registerApplication("org.onos.acl");
+        hostService.addListener(hostListener);
+        idGenerator = coreService.getIdGenerator("acl-ids");
+        AclRule.bindIdGenerator(idGenerator);
+        log.info("Started");
+    }
+
+    @Deactivate
+    public void deactivate() {
+        hostService.removeListener(hostListener);
+        flowRuleService.removeFlowRulesById(appId);
+        aclStore.clearAcl();
+        log.info("Stopped");
+    }
+
+    @Override
+    public List<AclRule> getAclRules() {
+        return aclStore.getAclRules();
+    }
+
+    /**
+     * Checks if the new ACL rule matches an existing rule.
+     * If existing allowing rules matches the new denying rule, store the mappings.
+     *
+     * @return true if the new ACL rule matches an existing rule, false otherwise
+     */
+    private boolean matchCheck(AclRule newRule) {
+        for (AclRule existingRule : aclStore.getAclRules()) {
+            if (newRule.checkMatch(existingRule)) {
+                return true;
+            }
+
+            if (existingRule.action() == AclRule.Action.ALLOW
+                    && newRule.action() == AclRule.Action.DENY) {
+                if (existingRule.checkMatch(newRule)) {
+                    aclStore.addDenyToAllowMapping(newRule.id(), existingRule.id());
+                }
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public boolean addAclRule(AclRule rule) {
+        if (matchCheck(rule)) {
+            return false;
+        }
+        aclStore.addAclRule(rule);
+        log.info("ACL rule(id:{}) is added.", rule.id());
+        if (rule.action() != AclRule.Action.ALLOW) {
+            enforceRuleAdding(rule);
+        }
+        return true;
+    }
+
+    /**
+     * Gets a set containing all devices connecting with the hosts
+     * whose IP address is in the given CIDR IP address.
+     */
+    private Set<DeviceId> getDeviceIdSet(Ip4Prefix cidrAddr) {
+        Set<DeviceId> deviceIdSet = new HashSet<>();
+        final Iterable<Host> hosts = hostService.getHosts();
+
+        if (cidrAddr.prefixLength() != 32) {
+            for (Host h : hosts) {
+                for (IpAddress a : h.ipAddresses()) {
+                    if (checkIpInCIDR(a.getIp4Address(), cidrAddr)) {
+                        deviceIdSet.add(h.location().deviceId());
+                    }
+                }
+            }
+        } else {
+            for (Host h : hosts) {
+                for (IpAddress a : h.ipAddresses()) {
+                    if (checkIpInCIDR(a.getIp4Address(), cidrAddr)) {
+                        deviceIdSet.add(h.location().deviceId());
+                        return deviceIdSet;
+                    }
+                }
+            }
+        }
+        return deviceIdSet;
+    }
+
+    /**
+     * Enforces denying ACL rule by ACL flow rules.
+     */
+    private void enforceRuleAdding(AclRule rule) {
+        Set<DeviceId> dpidSet;
+        if (rule.srcIp() != null) {
+            dpidSet = getDeviceIdSet(rule.srcIp());
+        } else {
+            dpidSet = getDeviceIdSet(rule.dstIp());
+        }
+
+        for (DeviceId deviceId : dpidSet) {
+            List<RuleId> allowingRuleList = aclStore.getAllowingRuleByDenyingRule(rule.id());
+            if (allowingRuleList != null) {
+                for (RuleId allowingRuleId : allowingRuleList) {
+                    generateACLFlow(aclStore.getAclRule(allowingRuleId), deviceId);
+                }
+            }
+            generateACLFlow(rule, deviceId);
+        }
+    }
+
+    /**
+     * Generates ACL flow rule according to ACL rule
+     * and install it into related device.
+     */
+    private void generateACLFlow(AclRule rule, DeviceId deviceId) {
+        if (rule == null || aclStore.checkIfRuleWorksInDevice(rule.id(), deviceId)) {
+            return;
+        }
+
+        TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
+        TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
+        FlowEntry.Builder flowEntry = DefaultFlowEntry.builder();
+
+        selectorBuilder.matchEthType(Ethernet.TYPE_IPV4);
+        if (rule.srcIp() != null) {
+            selectorBuilder.matchIPSrc(rule.srcIp());
+            if (rule.dstIp() != null) {
+                selectorBuilder.matchIPDst(rule.dstIp());
+            }
+        } else {
+            selectorBuilder.matchIPDst(rule.dstIp());
+        }
+        if (rule.ipProto() != 0) {
+            selectorBuilder.matchIPProtocol(Integer.valueOf(rule.ipProto()).byteValue());
+        }
+        if (rule.dstTpPort() != 0) {
+            switch (rule.ipProto()) {
+                case IPv4.PROTOCOL_TCP:
+                    selectorBuilder.matchTcpDst(TpPort.tpPort(rule.dstTpPort()));
+                    break;
+                case IPv4.PROTOCOL_UDP:
+                    selectorBuilder.matchUdpDst(TpPort.tpPort(rule.dstTpPort()));
+                    break;
+                default:
+                    break;
+            }
+        }
+        if (rule.action() == AclRule.Action.ALLOW) {
+            treatment.add(Instructions.createOutput(PortNumber.CONTROLLER));
+        }
+        flowEntry.forDevice(deviceId);
+        flowEntry.withPriority(aclStore.getPriorityByDevice(deviceId));
+        flowEntry.withSelector(selectorBuilder.build());
+        flowEntry.withTreatment(treatment.build());
+        flowEntry.fromApp(appId);
+        flowEntry.makePermanent();
+        // install flow rule
+        flowRuleService.applyFlowRules(flowEntry.build());
+        log.debug("ACL flow rule {} is installed in {}.", flowEntry.build(), deviceId);
+        aclStore.addRuleToFlowMapping(rule.id(), flowEntry.build());
+        aclStore.addRuleToDeviceMapping(rule.id(), deviceId);
+    }
+
+    @Override
+    public void removeAclRule(RuleId ruleId) {
+        aclStore.removeAclRule(ruleId);
+        log.info("ACL rule(id:{}) is removed.", ruleId);
+        enforceRuleRemoving(ruleId);
+    }
+
+    /**
+     * Enforces removing an existing ACL rule.
+     */
+    private void enforceRuleRemoving(RuleId ruleId) {
+        Set<FlowRule> flowSet = aclStore.getFlowByRule(ruleId);
+        if (flowSet != null) {
+            for (FlowRule flowRule : flowSet) {
+                flowRuleService.removeFlowRules(flowRule);
+                log.debug("ACL flow rule {} is removed from {}.", flowRule.toString(), flowRule.deviceId().toString());
+            }
+        }
+        aclStore.removeRuleToFlowMapping(ruleId);
+        aclStore.removeRuleToDeviceMapping(ruleId);
+        aclStore.removeDenyToAllowMapping(ruleId);
+    }
+
+    @Override
+    public void clearAcl() {
+        aclStore.clearAcl();
+        flowRuleService.removeFlowRulesById(appId);
+        log.info("ACL is cleared.");
+    }
+
+}
diff --git a/apps/acl/src/main/java/org/onosproject/acl/impl/DistributedAclStore.java b/apps/acl/src/main/java/org/onosproject/acl/impl/DistributedAclStore.java
new file mode 100644
index 0000000..a5fcfcc
--- /dev/null
+++ b/apps/acl/src/main/java/org/onosproject/acl/impl/DistributedAclStore.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ * Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China
+ * Advisers: Keqiu Li, Heng Qi and Haisheng Yu
+ * This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002)
+ * and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.acl.impl;
+
+import com.google.common.collect.Collections2;
+import org.onosproject.acl.AclRule;
+import org.onosproject.acl.AclStore;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.acl.RuleId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.store.AbstractStore;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.ConsistentMap;
+import org.onosproject.store.service.Serializer;
+import org.onosproject.store.service.StorageService;
+import org.onosproject.store.service.Versioned;
+import org.slf4j.Logger;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Implementation of the ACL store service.
+ */
+@Component(immediate = true)
+@Service
+public class DistributedAclStore extends AbstractStore implements AclStore {
+
+    private final Logger log = getLogger(getClass());
+    private final int defaultFlowMaxPriority = 30000;
+
+    private ConsistentMap<RuleId, AclRule> ruleSet;
+    private ConsistentMap<DeviceId, Integer> deviceToPriority;
+    private ConsistentMap<RuleId, Set<DeviceId>> ruleToDevice;
+    private ConsistentMap<RuleId, Set<FlowRule>> ruleToFlow;
+    private ConsistentMap<RuleId, List<RuleId>> denyRuleToAllowRule;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected StorageService storageService;
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Activate
+    public void activate() {
+        ApplicationId appId = coreService.getAppId("org.onosproject.acl");
+
+        KryoNamespace.Builder serializer = KryoNamespace.newBuilder()
+                .register(KryoNamespaces.API)
+                .register(AclRule.class)
+                .register(AclRule.Action.class)
+                .register(RuleId.class);
+
+        ruleSet = storageService.<RuleId, AclRule>consistentMapBuilder()
+                .withSerializer(Serializer.using(serializer.build()))
+                .withName("acl-rule-set")
+                .withApplicationId(appId)
+                .withPurgeOnUninstall()
+                .build();
+
+        deviceToPriority = storageService.<DeviceId, Integer>consistentMapBuilder()
+                .withSerializer(Serializer.using(serializer.build()))
+                .withName("device-to-priority")
+                .withApplicationId(appId)
+                .withPurgeOnUninstall()
+                .build();
+
+        ruleToFlow = storageService.<RuleId, Set<FlowRule>>consistentMapBuilder()
+                .withSerializer(Serializer.using(serializer.build()))
+                .withName("rule-to-flow")
+                .withApplicationId(appId)
+                .withPurgeOnUninstall()
+                .build();
+
+        denyRuleToAllowRule = storageService.<RuleId, List<RuleId>>consistentMapBuilder()
+                .withSerializer(Serializer.using(serializer.build()))
+                .withName("deny-to-allow")
+                .withApplicationId(appId)
+                .withPurgeOnUninstall()
+                .build();
+
+        ruleToDevice = storageService.<RuleId, Set<DeviceId>>consistentMapBuilder()
+                .withSerializer(Serializer.using(serializer.build()))
+                .withName("rule-to-device")
+                .withApplicationId(appId)
+                .withPurgeOnUninstall()
+                .build();
+
+        log.info("Started");
+    }
+
+    @Deactivate
+    public void deactive() {
+        log.info("Stopped");
+    }
+
+    @Override
+    public List<AclRule> getAclRules() {
+        List<AclRule> aclRules = new ArrayList<>();
+        aclRules.addAll(Collections2.transform(ruleSet.values(), Versioned::value));
+        return aclRules;
+    }
+
+    @Override
+    public void addAclRule(AclRule rule) {
+        ruleSet.putIfAbsent(rule.id(), rule);
+    }
+
+    @Override
+    public AclRule getAclRule(RuleId ruleId) {
+        Versioned<AclRule> rule = ruleSet.get(ruleId);
+        if (rule != null) {
+            return rule.value();
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public void removeAclRule(RuleId ruleId) {
+        ruleSet.remove(ruleId);
+    }
+
+    @Override
+    public void clearAcl() {
+        ruleSet.clear();
+        deviceToPriority.clear();
+        ruleToFlow.clear();
+        denyRuleToAllowRule.clear();
+        ruleToDevice.clear();
+    }
+
+    @Override
+    public int getPriorityByDevice(DeviceId deviceId) {
+        return deviceToPriority.compute(deviceId,
+                                        (id, priority) -> (priority == null) ? defaultFlowMaxPriority : (priority - 1))
+                .value();
+    }
+
+    @Override
+    public Set<FlowRule> getFlowByRule(RuleId ruleId) {
+        Versioned<Set<FlowRule>> flowRuleSet = ruleToFlow.get(ruleId);
+        if (flowRuleSet != null) {
+            return flowRuleSet.value();
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public void addRuleToFlowMapping(RuleId ruleId, FlowRule flowRule) {
+        ruleToFlow.computeIf(ruleId,
+                             flowRuleSet -> (flowRuleSet == null || !flowRuleSet.contains(flowRule)),
+                             (id, flowRuleSet) -> {
+                                 Set<FlowRule> newSet = new HashSet<>();
+                                 if (flowRuleSet != null) {
+                                     newSet.addAll(flowRuleSet);
+                                 }
+                                 newSet.add(flowRule);
+                                 return newSet;
+                             });
+    }
+
+    @Override
+    public void removeRuleToFlowMapping(RuleId ruleId) {
+        ruleToFlow.remove(ruleId);
+    }
+
+    @Override
+    public List<RuleId> getAllowingRuleByDenyingRule(RuleId denyingRuleId) {
+        Versioned<List<RuleId>> allowRuleIdSet = denyRuleToAllowRule.get(denyingRuleId);
+        if (allowRuleIdSet != null) {
+            return allowRuleIdSet.value();
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public void addDenyToAllowMapping(RuleId denyingRuleId, RuleId allowingRuleId) {
+        denyRuleToAllowRule.computeIf(denyingRuleId,
+                                      ruleIdList -> (ruleIdList == null || !ruleIdList.contains(allowingRuleId)),
+                                      (id, ruleIdList) -> {
+                                          ArrayList<RuleId> newList = new ArrayList<>();
+                                          if (ruleIdList != null) {
+                                              newList.addAll(ruleIdList);
+                                          }
+                                          newList.add(allowingRuleId);
+                                          return newList;
+                                      });
+    }
+
+    @Override
+    public void removeDenyToAllowMapping(RuleId denyingRuleId) {
+        denyRuleToAllowRule.remove(denyingRuleId);
+    }
+
+    @Override
+    public boolean checkIfRuleWorksInDevice(RuleId ruleId, DeviceId deviceId) {
+        return ruleToDevice.containsKey(ruleId) && ruleToDevice.get(ruleId).value().contains(deviceId);
+    }
+
+    @Override
+    public void addRuleToDeviceMapping(RuleId ruleId, DeviceId deviceId) {
+        ruleToDevice.computeIf(ruleId,
+                               deviceIdSet -> (deviceIdSet == null || !deviceIdSet.contains(deviceId)),
+                               (id, deviceIdSet) -> {
+                                   Set<DeviceId> newSet = new HashSet<>();
+                                   if (deviceIdSet != null) {
+                                       newSet.addAll(deviceIdSet);
+                                   }
+                                   newSet.add(deviceId);
+                                   return newSet;
+                               });
+    }
+
+    @Override
+    public void removeRuleToDeviceMapping(RuleId ruleId) {
+        ruleToDevice.remove(ruleId);
+    }
+
+}
diff --git a/apps/acl/src/main/java/org/onosproject/acl/impl/package-info.java b/apps/acl/src/main/java/org/onosproject/acl/impl/package-info.java
new file mode 100644
index 0000000..9da9b3b
--- /dev/null
+++ b/apps/acl/src/main/java/org/onosproject/acl/impl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * ACL application implementation.
+ */
+package org.onosproject.acl.impl;