From 13d05bc8458758ee39cb829098241e89616717ee Mon Sep 17 00:00:00 2001 From: Ashlee Young Date: Wed, 9 Sep 2015 22:15:21 -0700 Subject: ONOS checkin based on commit tag e796610b1f721d02f9b0e213cf6f7790c10ecd60 Change-Id: Ife8810491034fe7becdba75dda20de4267bd15cd --- framework/src/onos/apps/acl/pom.xml | 145 +++++++++ .../acl/src/main/java/org/onos/acl/AclRule.java | 302 ++++++++++++++++++ .../acl/src/main/java/org/onos/acl/AclService.java | 53 ++++ .../acl/src/main/java/org/onos/acl/AclStore.java | 132 ++++++++ .../src/main/java/org/onos/acl/AclWebResource.java | 216 +++++++++++++ .../acl/src/main/java/org/onos/acl/RuleId.java | 85 ++++++ .../main/java/org/onos/acl/impl/AclManager.java | 337 +++++++++++++++++++++ .../org/onos/acl/impl/DistributedAclStore.java | 251 +++++++++++++++ .../onos/apps/acl/src/main/webapp/WEB-INF/web.xml | 46 +++ .../java/org/onos/acl/web/AclWebResourceTest.java | 133 ++++++++ .../test/java/org/onos/acl/web/ResourceTest.java | 58 ++++ .../org/onos/acl/web/TestServiceDirectory.java | 51 ++++ 12 files changed, 1809 insertions(+) create mode 100644 framework/src/onos/apps/acl/pom.xml create mode 100644 framework/src/onos/apps/acl/src/main/java/org/onos/acl/AclRule.java create mode 100644 framework/src/onos/apps/acl/src/main/java/org/onos/acl/AclService.java create mode 100644 framework/src/onos/apps/acl/src/main/java/org/onos/acl/AclStore.java create mode 100644 framework/src/onos/apps/acl/src/main/java/org/onos/acl/AclWebResource.java create mode 100644 framework/src/onos/apps/acl/src/main/java/org/onos/acl/RuleId.java create mode 100644 framework/src/onos/apps/acl/src/main/java/org/onos/acl/impl/AclManager.java create mode 100644 framework/src/onos/apps/acl/src/main/java/org/onos/acl/impl/DistributedAclStore.java create mode 100644 framework/src/onos/apps/acl/src/main/webapp/WEB-INF/web.xml create mode 100644 framework/src/onos/apps/acl/src/test/java/org/onos/acl/web/AclWebResourceTest.java create mode 100644 framework/src/onos/apps/acl/src/test/java/org/onos/acl/web/ResourceTest.java create mode 100644 framework/src/onos/apps/acl/src/test/java/org/onos/acl/web/TestServiceDirectory.java (limited to 'framework/src/onos/apps/acl') diff --git a/framework/src/onos/apps/acl/pom.xml b/framework/src/onos/apps/acl/pom.xml new file mode 100644 index 00000000..20f784fc --- /dev/null +++ b/framework/src/onos/apps/acl/pom.xml @@ -0,0 +1,145 @@ + + + + 4.0.0 + + + org.onosproject + onos-apps + 1.3.0-SNAPSHOT + ../pom.xml + + + onos-app-acl + 1.3.0-SNAPSHOT + bundle + + ONOS ACL application + http://onosproject.org + + + 1.3.0-SNAPSHOT + org.onosproject.acl + DLUT + /onos/acl + 1.0.0 + ONOS ACL Application REST API + + APIs for interacting with the ACL application. + + org.onos.acl.impl + + + + + com.google.guava + guava + 18.0 + + + + javax.ws.rs + jsr311-api + 1.1.1 + + + + org.onosproject + onlab-junit + ${onos.version} + + + + org.onosproject + onos-rest + ${onos.version} + + + + org.onosproject + onos-core-serializers + ${onos.version} + + + + com.sun.jersey.jersey-test-framework + jersey-test-framework-core + 1.19 + + + + org.easymock + easymock + 3.2 + + + + com.sun.jersey.jersey-test-framework + jersey-test-framework-grizzly2 + 1.19 + + + + org.onosproject + onlab-misc + ${onos.version} + + + + + + + org.apache.felix + maven-bundle-plugin + 2.5.3 + true + + + <_wab>src/main/webapp/ + + WEB-INF/classes/apidoc/swagger.json=target/swagger.json, + {maven-resources} + + + ${project.groupId}.${project.artifactId} + + + org.slf4j, + org.osgi.framework, + javax.ws.rs,javax.ws.rs.core, + com.fasterxml.jackson*, + com.sun.jersey.api.core, + com.sun.jersey.spi.container.servlet, + com.sun.jersey.server.impl.container.servlet, + org.onlab.packet.*, + org.onlab.rest.*, + org.onosproject.*, + org.onlab.util.*, + com.google.common.*; + + ${web.context} + + + + + + + diff --git a/framework/src/onos/apps/acl/src/main/java/org/onos/acl/AclRule.java b/framework/src/onos/apps/acl/src/main/java/org/onos/acl/AclRule.java new file mode 100644 index 00000000..edfa9355 --- /dev/null +++ b/framework/src/onos/apps/acl/src/main/java/org/onos/acl/AclRule.java @@ -0,0 +1,302 @@ +/* + * 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.onos.acl; + +import com.google.common.base.MoreObjects; +import org.onlab.packet.IPv4; +import org.onlab.packet.Ip4Prefix; +import org.onosproject.core.IdGenerator; + +import java.util.Objects; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +/** + * ACL rule class. + */ +public final class AclRule { + + private final RuleId id; + + private final Ip4Prefix srcIp; + private final Ip4Prefix dstIp; + private final byte ipProto; + private final short dstTpPort; + private final Action action; + + private static IdGenerator idGenerator; + + /** + * Enum type for ACL rule's action. + */ + public enum Action { + DENY, ALLOW + } + + /** + * Constructor for serializer. + */ + private AclRule() { + this.id = null; + this.srcIp = null; + this.dstIp = null; + this.ipProto = 0; + this.dstTpPort = 0; + this.action = null; + } + + /** + * Create a new ACL rule. + * + * @param srcIp source IP address + * @param dstIp destination IP address + * @param ipProto IP protocol + * @param dstTpPort destination transport layer port + * @param action ACL rule's action + */ + private AclRule(Ip4Prefix srcIp, + Ip4Prefix dstIp, + byte ipProto, + short dstTpPort, + Action action) { + checkState(idGenerator != null, "Id generator is not bound."); + this.id = RuleId.valueOf(idGenerator.getNewId()); + this.srcIp = srcIp; + this.dstIp = dstIp; + this.ipProto = ipProto; + this.dstTpPort = dstTpPort; + this.action = action; + } + + /** + * Check if the first CIDR address is in (or the same as) the second CIDR address. + */ + private boolean checkCIDRinCIDR(Ip4Prefix cidrAddr1, Ip4Prefix cidrAddr2) { + if (cidrAddr2 == null) { + return true; + } else if (cidrAddr1 == null) { + return false; + } + if (cidrAddr1.prefixLength() < cidrAddr2.prefixLength()) { + return false; + } + int offset = 32 - cidrAddr2.prefixLength(); + + int cidr1Prefix = cidrAddr1.address().toInt(); + int cidr2Prefix = cidrAddr2.address().toInt(); + cidr1Prefix = cidr1Prefix >> offset; + cidr2Prefix = cidr2Prefix >> offset; + cidr1Prefix = cidr1Prefix << offset; + cidr2Prefix = cidr2Prefix << offset; + + return (cidr1Prefix == cidr2Prefix); + } + + /** + * Check if this ACL rule match the given ACL rule. + * @param r ACL rule to check against + * @return true if this ACL rule matches the given ACL ruleule. + */ + public boolean checkMatch(AclRule r) { + return (this.dstTpPort == r.dstTpPort || r.dstTpPort == 0) + && (this.ipProto == r.ipProto || r.ipProto == 0) + && (checkCIDRinCIDR(this.srcIp(), r.srcIp())) + && (checkCIDRinCIDR(this.dstIp(), r.dstIp())); + } + + /** + * Returns a new ACL rule builder. + * + * @return ACL rule builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Builder of an ACL rule. + */ + public static final class Builder { + + private Ip4Prefix srcIp = null; + private Ip4Prefix dstIp = null; + private byte ipProto = 0; + private short dstTpPort = 0; + private Action action = Action.DENY; + + private Builder() { + // Hide constructor + } + + /** + * Sets the source IP address for the ACL rule that will be built. + * + * @param srcIp source IP address to use for built ACL rule + * @return this builder + */ + public Builder srcIp(String srcIp) { + this.srcIp = Ip4Prefix.valueOf(srcIp); + return this; + } + + /** + * Sets the destination IP address for the ACL rule that will be built. + * + * @param dstIp destination IP address to use for built ACL rule + * @return this builder + */ + public Builder dstIp(String dstIp) { + this.dstIp = Ip4Prefix.valueOf(dstIp); + return this; + } + + /** + * Sets the IP protocol for the ACL rule that will be built. + * + * @param ipProto IP protocol to use for built ACL rule + * @return this builder + */ + public Builder ipProto(byte ipProto) { + this.ipProto = ipProto; + return this; + } + + /** + * Sets the destination transport layer port for the ACL rule that will be built. + * + * @param dstTpPort destination transport layer port to use for built ACL rule + * @return this builder + */ + public Builder dstTpPort(short dstTpPort) { + if ((ipProto == IPv4.PROTOCOL_TCP || ipProto == IPv4.PROTOCOL_UDP)) { + this.dstTpPort = dstTpPort; + } + return this; + } + + /** + * Sets the action for the ACL rule that will be built. + * + * @param action action to use for built ACL rule + * @return this builder + */ + public Builder action(Action action) { + this.action = action; + return this; + } + + /** + * Builds an ACL rule from the accumulated parameters. + * @return ACL rule instance + */ + public AclRule build() { + checkState(srcIp != null && dstIp != null, "Either srcIp or dstIp must be assigned."); + 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 + ); + } + + } + + /** + * Binds an id generator for unique ACL rule id generation. + * + * Note: A generator cannot be bound if there is already a generator bound. + * + * @param newIdGenerator id generator + */ + public static void bindIdGenerator(IdGenerator newIdGenerator) { + checkState(idGenerator == null, "Id generator is already bound."); + idGenerator = checkNotNull(newIdGenerator); + } + + public RuleId id() { + return id; + } + + public Ip4Prefix srcIp() { + return srcIp; + } + + public Ip4Prefix dstIp() { + return this.dstIp; + } + + public byte ipProto() { + return ipProto; + } + + public short dstTpPort() { + return dstTpPort; + } + + public Action action() { + return action; + } + + @Override + public int hashCode() { + return Objects.hash(action, + id.fingerprint(), + ipProto, + srcIp, + dstIp, + dstTpPort); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof AclRule) { + AclRule that = (AclRule) obj; + return Objects.equals(id, that.id) && + Objects.equals(srcIp, that.srcIp) && + Objects.equals(dstIp, that.dstIp) && + Objects.equals(ipProto, that.ipProto) && + Objects.equals(dstTpPort, that.dstTpPort) && + Objects.equals(action, that.action); + } + return false; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .omitNullValues() + .add("id", id) + .add("srcIp", srcIp) + .add("dstIp", dstIp) + .add("ipProto", ipProto) + .add("dstTpPort", dstTpPort) + .add("action", action) + .toString(); + } + +} diff --git a/framework/src/onos/apps/acl/src/main/java/org/onos/acl/AclService.java b/framework/src/onos/apps/acl/src/main/java/org/onos/acl/AclService.java new file mode 100644 index 00000000..afa561e4 --- /dev/null +++ b/framework/src/onos/apps/acl/src/main/java/org/onos/acl/AclService.java @@ -0,0 +1,53 @@ +/* + * 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.onos.acl; + +import java.util.List; + +/** + * Service interface exported by ACL application. + */ +public interface AclService { + + /** + * Gets a list containing all ACL rules. + * @return a list containing all ACL rules + */ + List getAclRules(); + + /** + * Adds a new ACL rule. + * @param rule ACL rule + * @return true if successfully added, otherwise false + */ + boolean addAclRule(AclRule rule); + + /** + * Removes an exsiting ACL rule by rule id. + * @param ruleId ACL rule identifier + */ + void removeAclRule(RuleId ruleId); + + /** + * Clears ACL and resets all. + */ + void clearAcl(); + +} \ No newline at end of file diff --git a/framework/src/onos/apps/acl/src/main/java/org/onos/acl/AclStore.java b/framework/src/onos/apps/acl/src/main/java/org/onos/acl/AclStore.java new file mode 100644 index 00000000..88e49a73 --- /dev/null +++ b/framework/src/onos/apps/acl/src/main/java/org/onos/acl/AclStore.java @@ -0,0 +1,132 @@ +/* + * 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.onos.acl; + +import org.onosproject.net.DeviceId; +import org.onosproject.net.flow.FlowRule; +import org.onosproject.store.Store; + +import java.util.List; +import java.util.Set; + +/** + * Service interface exported by ACL distributed store. + */ +public interface AclStore extends Store { + + /** + * Gets a list containing all ACL rules. + * @return a list containing all ACL rules + */ + List getAclRules(); + + /** + * Adds a new ACL rule. + * @param rule new ACL rule + */ + void addAclRule(AclRule rule); + + /** + * Gets an existing ACL rule. + * @param ruleId ACL rule id + * @return ACL rule with the given id + */ + AclRule getAclRule(RuleId ruleId); + + /** + * Removes an existing ACL rule by rule id. + * @param ruleId ACL rule id + */ + void removeAclRule(RuleId ruleId); + + /** + * Clears ACL and reset all. + */ + void clearAcl(); + + /** + * Gets the current priority for new ACL flow rule by device id. + * @param deviceId device id + * @return new ACL flow rule's priority in the given device + */ + int getPriorityByDevice(DeviceId deviceId); + + /** + * Gets a set containing all ACL flow rules belonging to a given ACL rule. + * @param ruleId ACL rule id + * @return a set containing all ACL flow rules belonging to the given ACL rule + */ + Set getFlowByRule(RuleId ruleId); + + /** + * Adds a new mapping from ACL rule to ACL flow rule. + * @param ruleId ACL rule id + * @param flowRule ACL flow rule + */ + void addRuleToFlowMapping(RuleId ruleId, FlowRule flowRule); + + /** + * Removes an existing mapping from ACL rule to ACL flow rule. + * @param ruleId ACL rule id + */ + void removeRuleToFlowMapping(RuleId ruleId); + + /** + * Gets a list containing all allowing ACL rules matching a given denying ACL rule. + * @param denyingRuleId denying ACL rule id + * @return a list containing all allowing ACL rules matching the given denying ACL rule + */ + List getAllowingRuleByDenyingRule(RuleId denyingRuleId); + + /** + * Adds a new mapping from denying ACL rule to allowing ACL rule. + * @param denyingRuleId denying ACL rule id + * @param allowingRuleId allowing ACL rule id + */ + void addDenyToAllowMapping(RuleId denyingRuleId, RuleId allowingRuleId); + + /** + * Removes an exsiting mapping from denying ACL rule to allowing ACL rule. + * @param denyingRuleId denying ACL rule id + */ + void removeDenyToAllowMapping(RuleId denyingRuleId); + + /** + * Checks if an existing ACL rule already works in a given device. + * @param ruleId ACL rule id + * @param deviceId devide id + * @return true if the given ACL rule works in the given device + */ + boolean checkIfRuleWorksInDevice(RuleId ruleId, DeviceId deviceId); + + /** + * Adds a new mapping from ACL rule to device. + * @param ruleId ACL rule id + * @param deviceId device id + */ + void addRuleToDeviceMapping(RuleId ruleId, DeviceId deviceId); + + /** + * Removes an existing mapping from ACL rule to device. + * @param ruleId ACL rule id + */ + void removeRuleToDeviceMapping(RuleId ruleId); + +} diff --git a/framework/src/onos/apps/acl/src/main/java/org/onos/acl/AclWebResource.java b/framework/src/onos/apps/acl/src/main/java/org/onos/acl/AclWebResource.java new file mode 100644 index 00000000..3f4dc769 --- /dev/null +++ b/framework/src/onos/apps/acl/src/main/java/org/onos/acl/AclWebResource.java @@ -0,0 +1,216 @@ +/* + * 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.onos.acl; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.onlab.packet.IPv4; +import org.onosproject.rest.AbstractWebResource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import java.io.IOException; +import java.io.InputStream; +import java.util.List; + +// FIXME: This does now follow REST-full principles and should be refactored. +/** + * Manage ACL rules. + */ +@Path("") +public class AclWebResource extends AbstractWebResource { + + private final Logger log = LoggerFactory.getLogger(getClass()); + + /** + * Processes user's GET HTTP request for querying ACL rules. + * @return response to the request + */ + @GET + public Response queryAclRule() { + List rules = get(AclService.class).getAclRules(); + ObjectMapper mapper = new ObjectMapper(); + ObjectNode root = mapper.createObjectNode(); + ArrayNode arrayNode = mapper.createArrayNode(); + for (AclRule rule : rules) { + ObjectNode node = mapper.createObjectNode(); + node.put("id", rule.id().toString()); + if (rule.srcIp() != null) { + node.put("srcIp", rule.srcIp().toString()); + } + if (rule.dstIp() != null) { + node.put("dstIp", rule.dstIp().toString()); + } + if (rule.ipProto() != 0) { + switch (rule.ipProto()) { + case IPv4.PROTOCOL_ICMP: + node.put("ipProto", "ICMP"); + break; + case IPv4.PROTOCOL_TCP: + node.put("ipProto", "TCP"); + break; + case IPv4.PROTOCOL_UDP: + node.put("ipProto", "UDP"); + break; + default: + break; + } + } + if (rule.dstTpPort() != 0) { + node.put("dstTpPort", rule.dstTpPort()); + } + node.put("action", rule.action().toString()); + arrayNode.add(node); + } + root.set("ACL rules", arrayNode); + return Response.ok(root.toString(), MediaType.APPLICATION_JSON_TYPE).build(); + } + + /** + * Processes user's POST HTTP request for add ACL rules. + * @param stream input stream + * @return response to the request + */ + @POST + @Path("add") + public Response addAclRule(InputStream stream) { + AclRule newRule; + try { + newRule = jsonToRule(stream); + } catch (Exception e) { + return Response.ok("{\"status\" : \"Failed! " + e.getMessage() + "\"}").build(); + } + + String status; + if (get(AclService.class).addAclRule(newRule)) { + status = "Success! New ACL rule is added."; + } else { + status = "Failed! New ACL rule matches an existing rule."; + } + return Response.ok("{\"status\" : \"" + status + "\"}").build(); + } + + /** + * Processes user's GET HTTP request for removing ACL rule. + * @param id ACL rule id (in hex string format) + * @return response to the request + */ + @GET + @Path("remove/{id}") + public Response removeAclRule(@PathParam("id") String id) { + String status; + RuleId ruleId = new RuleId(Long.parseLong(id.substring(2), 16)); + if (get(AclStore.class).getAclRule(ruleId) == null) { + status = "Failed! There is no ACL rule with this id."; + } else { + get(AclService.class).removeAclRule(ruleId); + status = "Success! ACL rule(id:" + id + ") is removed."; + } + return Response.ok("{\"status\" : \"" + status + "\"}").build(); + } + + /** + * Processes user's GET HTTP request for clearing ACL. + * @return response to the request + */ + @GET + @Path("clear") + public Response clearACL() { + get(AclService.class).clearAcl(); + return Response.ok("{\"status\" : \"ACL is cleared.\"}").build(); + } + + /** + * Exception class for parsing a invalid ACL rule. + */ + private class AclRuleParseException extends Exception { + public AclRuleParseException(String message) { + super(message); + } + } + + /** + * Turns a JSON string into an ACL rule instance. + */ + private AclRule jsonToRule(InputStream stream) throws AclRuleParseException, IOException { + ObjectMapper mapper = new ObjectMapper(); + JsonNode jsonNode = mapper.readTree(stream); + JsonParser jp = jsonNode.traverse(); + AclRule.Builder rule = AclRule.builder(); + jp.nextToken(); + if (jp.getCurrentToken() != JsonToken.START_OBJECT) { + throw new AclRuleParseException("Expected START_OBJECT"); + } + + while (jp.nextToken() != JsonToken.END_OBJECT) { + if (jp.getCurrentToken() != JsonToken.FIELD_NAME) { + throw new AclRuleParseException("Expected FIELD_NAME"); + } + + String key = jp.getCurrentName(); + jp.nextToken(); + String value = jp.getText(); + if ("".equals(value)) { + continue; + } + + if ("srcIp".equals(key)) { + rule.srcIp(value); + } else if ("dstIp".equals(key)) { + rule.dstIp(value); + } else if ("ipProto".equals(key)) { + if ("TCP".equalsIgnoreCase(value)) { + rule.ipProto(IPv4.PROTOCOL_TCP); + } else if ("UDP".equalsIgnoreCase(value)) { + rule.ipProto(IPv4.PROTOCOL_UDP); + } else if ("ICMP".equalsIgnoreCase(value)) { + rule.ipProto(IPv4.PROTOCOL_ICMP); + } else { + throw new AclRuleParseException("ipProto must be assigned to TCP, UDP, or ICMP."); + } + } else if ("dstTpPort".equals(key)) { + try { + rule.dstTpPort(Short.parseShort(value)); + } catch (NumberFormatException e) { + throw new AclRuleParseException("dstTpPort must be assigned to a numerical value."); + } + } else if ("action".equals(key)) { + if (!"allow".equalsIgnoreCase(value) && !"deny".equalsIgnoreCase(value)) { + throw new AclRuleParseException("action must be assigned to ALLOW or DENY."); + } + if ("allow".equalsIgnoreCase(value)) { + rule.action(AclRule.Action.ALLOW); + } + } + } + return rule.build(); + } + +} \ No newline at end of file diff --git a/framework/src/onos/apps/acl/src/main/java/org/onos/acl/RuleId.java b/framework/src/onos/apps/acl/src/main/java/org/onos/acl/RuleId.java new file mode 100644 index 00000000..754a6435 --- /dev/null +++ b/framework/src/onos/apps/acl/src/main/java/org/onos/acl/RuleId.java @@ -0,0 +1,85 @@ +/* + * Copyright 2015 Open Networking Laboratory + * Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China + * Advisers: Keqiu Li and Heng Qi + * 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.onos.acl; + +/** + * ACL rule identifier suitable as an external key. + *

This class is immutable.

+ */ +public final class RuleId { + private final long value; + + /** + * Creates an ACL rule identifier from the specified long value. + * + * @param value long value + * @return ACL rule identifier + */ + public static RuleId valueOf(long value) { + return new RuleId(value); + } + + /** + * Constructor for serializer. + */ + RuleId() { + this.value = 0; + } + + /** + * Constructs the ID corresponding to a given long value. + * + * @param value the underlying value of this ID + */ + RuleId(long value) { + this.value = value; + } + + /** + * Returns the backing value. + * + * @return the value + */ + public long fingerprint() { + return value; + } + + @Override + public int hashCode() { + return Long.hashCode(value); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof RuleId)) { + return false; + } + RuleId that = (RuleId) obj; + return this.value == that.value; + } + + @Override + public String toString() { + return "0x" + Long.toHexString(value); + } +} diff --git a/framework/src/onos/apps/acl/src/main/java/org/onos/acl/impl/AclManager.java b/framework/src/onos/apps/acl/src/main/java/org/onos/acl/impl/AclManager.java new file mode 100644 index 00000000..0ffd4bcd --- /dev/null +++ b/framework/src/onos/apps/acl/src/main/java/org/onos/acl/impl/AclManager.java @@ -0,0 +1,337 @@ +/* + * 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.onos.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.onos.acl.AclRule; +import org.onos.acl.AclService; +import org.onos.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.onos.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 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 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 getDeviceIdSet(Ip4Prefix cidrAddr) { + Set deviceIdSet = new HashSet<>(); + final Iterable 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 dpidSet; + if (rule.srcIp() != null) { + dpidSet = getDeviceIdSet(rule.srcIp()); + } else { + dpidSet = getDeviceIdSet(rule.dstIp()); + } + + for (DeviceId deviceId : dpidSet) { + List 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 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/framework/src/onos/apps/acl/src/main/java/org/onos/acl/impl/DistributedAclStore.java b/framework/src/onos/apps/acl/src/main/java/org/onos/acl/impl/DistributedAclStore.java new file mode 100644 index 00000000..6d909a97 --- /dev/null +++ b/framework/src/onos/apps/acl/src/main/java/org/onos/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.onos.acl.impl; + +import com.google.common.collect.Collections2; +import org.onos.acl.AclRule; +import org.onos.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.onos.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 ruleSet; + private ConsistentMap deviceToPriority; + private ConsistentMap> ruleToDevice; + private ConsistentMap> ruleToFlow; + private ConsistentMap> 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.consistentMapBuilder() + .withSerializer(Serializer.using(serializer.build())) + .withName("acl-rule-set") + .withApplicationId(appId) + .withPurgeOnUninstall() + .build(); + + deviceToPriority = storageService.consistentMapBuilder() + .withSerializer(Serializer.using(serializer.build())) + .withName("device-to-priority") + .withApplicationId(appId) + .withPurgeOnUninstall() + .build(); + + ruleToFlow = storageService.>consistentMapBuilder() + .withSerializer(Serializer.using(serializer.build())) + .withName("rule-to-flow") + .withApplicationId(appId) + .withPurgeOnUninstall() + .build(); + + denyRuleToAllowRule = storageService.>consistentMapBuilder() + .withSerializer(Serializer.using(serializer.build())) + .withName("deny-to-allow") + .withApplicationId(appId) + .withPurgeOnUninstall() + .build(); + + ruleToDevice = storageService.>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 getAclRules() { + List 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 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 getFlowByRule(RuleId ruleId) { + Versioned> 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 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 getAllowingRuleByDenyingRule(RuleId denyingRuleId) { + Versioned> 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 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 newSet = new HashSet(); + if (deviceIdSet != null) { + newSet.addAll(deviceIdSet); + } + newSet.add(deviceId); + return newSet; + }); + } + + @Override + public void removeRuleToDeviceMapping(RuleId ruleId) { + ruleToDevice.remove(ruleId); + } + +} \ No newline at end of file diff --git a/framework/src/onos/apps/acl/src/main/webapp/WEB-INF/web.xml b/framework/src/onos/apps/acl/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000..2c2d5cf3 --- /dev/null +++ b/framework/src/onos/apps/acl/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,46 @@ + + + + ACL application + + + JAX-RS Service + com.sun.jersey.spi.container.servlet.ServletContainer + + com.sun.jersey.config.property.resourceConfigClass + com.sun.jersey.api.core.ClassNamesResourceConfig + + + com.sun.jersey.config.property.classnames + org.onos.acl.AclWebResource + + 10 + + + + JAX-RS Service + /* + + + diff --git a/framework/src/onos/apps/acl/src/test/java/org/onos/acl/web/AclWebResourceTest.java b/framework/src/onos/apps/acl/src/test/java/org/onos/acl/web/AclWebResourceTest.java new file mode 100644 index 00000000..bb7d8051 --- /dev/null +++ b/framework/src/onos/apps/acl/src/test/java/org/onos/acl/web/AclWebResourceTest.java @@ -0,0 +1,133 @@ +/* + * Copyright 2015 Open Networking Laboratory + * Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China + * Advisers: Keqiu Li and Heng Qi + * 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.onos.acl.web; + +import com.sun.jersey.api.client.WebResource; +import org.onos.acl.AclService; +import org.onos.acl.AclStore; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.onlab.osgi.ServiceDirectory; +import org.onlab.rest.BaseResource; +import org.onos.acl.AclRule; +import org.onosproject.core.IdGenerator; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicLong; + +import static org.easymock.EasyMock.*; +import static org.hamcrest.Matchers.containsString; +import static org.junit.Assert.assertThat; + +/** + * Test class for ACL application REST resource. + */ +public class AclWebResourceTest extends ResourceTest { + + final AclService mockAclService = createMock(AclService.class); + final AclStore mockAclStore = createMock(AclStore.class); + final List rules = new ArrayList<>(); + + @Before + public void setUp() { + expect(mockAclService.getAclRules()).andReturn(rules).anyTimes(); + ServiceDirectory testDirectory = new TestServiceDirectory().add(AclService.class, mockAclService) + .add(AclStore.class, mockAclStore); + BaseResource.setServiceDirectory(testDirectory); + } + + @After + public void tearDown() { + verify(mockAclService); + } + + /** + * Mock id generator for testing. + */ + private class MockIdGenerator implements IdGenerator { + private AtomicLong nextId = new AtomicLong(0); + + @Override + public long getNewId() { + return nextId.getAndIncrement(); + } + } + + @Test + public void testaddRule() throws IOException { + WebResource rs = resource(); + String response; + String json; + IdGenerator idGenerator = new MockIdGenerator(); + AclRule.bindIdGenerator(idGenerator); + + replay(mockAclService); + + // input a invalid JSON string that contains neither nw_src and nw_dst + json = "{\"ipProto\":\"TCP\",\"dstTpPort\":\"80\"}"; + response = rs.path("add").post(String.class, json); + assertThat(response, containsString("Failed! Either srcIp or dstIp must be assigned.")); + + // input a invalid JSON string that doesn't contain CIDR mask bits + json = "{\"ipProto\":\"TCP\",\"srcIp\":\"10.0.0.1\",\"dstTpPort\":\"80\",\"action\":\"DENY\"}"; + response = rs.path("add").post(String.class, json); + assertThat(response, containsString("Malformed IPv4 prefix string: 10.0.0.1. " + + "Address must take form \"x.x.x.x/y\"")); + + // input a invalid JSON string that contains a invalid IP address + json = "{\"ipProto\":\"TCP\",\"srcIp\":\"10.0.0.256/32\",\"dstTpPort\":\"80\",\"action\":\"DENY\"}"; + response = rs.path("add").post(String.class, json); + assertThat(response, containsString("Invalid IP address string: 10.0.0.256")); + + // input a invalid JSON string that contains a invalid IP address + json = "{\"ipProto\":\"TCP\",\"srcIp\":\"10.0.01/32\",\"dstTpPort\":\"80\",\"action\":\"DENY\"}"; + response = rs.path("add").post(String.class, json); + assertThat(response, containsString("Invalid IP address string: 10.0.01")); + + // input a invalid JSON string that contains a invalid CIDR mask bits + json = "{\"ipProto\":\"TCP\",\"srcIp\":\"10.0.0.1/a\",\"dstTpPort\":\"80\",\"action\":\"DENY\"}"; + response = rs.path("add").post(String.class, json); + assertThat(response, containsString("Failed! For input string: \"a\"")); + + // input a invalid JSON string that contains a invalid CIDR mask bits + json = "{\"ipProto\":\"TCP\",\"srcIp\":\"10.0.0.1/33\",\"dstTpPort\":\"80\",\"action\":\"DENY\"}"; + response = rs.path("add").post(String.class, json); + assertThat(response, containsString("Invalid prefix length 33. The value must be in the interval [0, 32]")); + + // input a invalid JSON string that contains a invalid ipProto value + json = "{\"ipProto\":\"ARP\",\"srcIp\":\"10.0.0.1/32\",\"dstTpPort\":\"80\",\"action\":\"DENY\"}"; + response = rs.path("add").post(String.class, json); + assertThat(response, containsString("ipProto must be assigned to TCP, UDP, or ICMP.")); + + // input a invalid JSON string that contains a invalid dstTpPort value + json = "{\"ipProto\":\"TCP\",\"srcIp\":\"10.0.0.1/32\",\"dstTpPort\":\"a\",\"action\":\"DENY\"}"; + response = rs.path("add").post(String.class, json); + assertThat(response, containsString("dstTpPort must be assigned to a numerical value.")); + + // input a invalid JSON string that contains a invalid action value + json = "{\"ipProto\":\"TCP\",\"srcIp\":\"10.0.0.1/32\",\"dstTpPort\":\"80\",\"action\":\"PERMIT\"}"; + response = rs.path("add").post(String.class, json); + assertThat(response, containsString("action must be assigned to ALLOW or DENY.")); + } +} diff --git a/framework/src/onos/apps/acl/src/test/java/org/onos/acl/web/ResourceTest.java b/framework/src/onos/apps/acl/src/test/java/org/onos/acl/web/ResourceTest.java new file mode 100644 index 00000000..04cd10bd --- /dev/null +++ b/framework/src/onos/apps/acl/src/test/java/org/onos/acl/web/ResourceTest.java @@ -0,0 +1,58 @@ +/* + * Copyright 2015 Open Networking Laboratory + * Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China + * Advisers: Keqiu Li and Heng Qi + * 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.onos.acl.web; + +import com.sun.jersey.test.framework.AppDescriptor; +import com.sun.jersey.test.framework.JerseyTest; +import com.sun.jersey.test.framework.WebAppDescriptor; + +import java.io.IOException; +import java.net.ServerSocket; + +/** + * Base class for REST API tests. Performs common configuration operations. + */ +public class ResourceTest extends JerseyTest { + + /** + * Assigns an available port for the test. + * + * @param defaultPort If a port cannot be determined, this one is used. + * @return free port + */ + @Override + public int getPort(int defaultPort) { + try { + ServerSocket socket = new ServerSocket(0); + socket.setReuseAddress(true); + int port = socket.getLocalPort(); + socket.close(); + return port; + } catch (IOException ioe) { + return defaultPort; + } + } + + @Override + public AppDescriptor configure() { + return new WebAppDescriptor.Builder("org.onos.acl").build(); + } + +} diff --git a/framework/src/onos/apps/acl/src/test/java/org/onos/acl/web/TestServiceDirectory.java b/framework/src/onos/apps/acl/src/test/java/org/onos/acl/web/TestServiceDirectory.java new file mode 100644 index 00000000..6dbd302c --- /dev/null +++ b/framework/src/onos/apps/acl/src/test/java/org/onos/acl/web/TestServiceDirectory.java @@ -0,0 +1,51 @@ +/* + * Copyright 2015 Open Networking Laboratory + * Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China + * Advisers: Keqiu Li and Heng Qi + * 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.onos.acl.web; + +import com.google.common.collect.ClassToInstanceMap; +import com.google.common.collect.MutableClassToInstanceMap; +import org.onlab.osgi.ServiceDirectory; + +/** + * Service directory implementation suitable for testing. + */ +public class TestServiceDirectory implements ServiceDirectory { + + + private ClassToInstanceMap services = MutableClassToInstanceMap.create(); + + @Override + public T get(Class serviceClass) { + return services.getInstance(serviceClass); + } + + /** + * Adds a new service to the directory. + * + * @param serviceClass service class + * @param service service instance + * @return self + */ + public TestServiceDirectory add(Class serviceClass, Object service) { + services.putInstance(serviceClass, service); + return this; + } + +} -- cgit 1.2.3-korg