diff options
author | Ashlee Young <ashlee@onosfw.com> | 2015-10-09 18:32:44 -0700 |
---|---|---|
committer | Ashlee Young <ashlee@onosfw.com> | 2015-10-09 18:32:44 -0700 |
commit | 6a07d2d622eaa06953f3353e39c080984076e8de (patch) | |
tree | bfb50a2090fce186c2cc545a400c969bf2ea702b /framework/src/onos/apps/acl | |
parent | e6d71622143ff9b2421a1abbe8434b954b5b1099 (diff) |
Updated master to commit id 6ee8aa3e67ce89908a8c93aa9445c6f71a18f986
Change-Id: I94b055ee2f298daf71e2ec794fd0f2495bd8081f
Diffstat (limited to 'framework/src/onos/apps/acl')
12 files changed, 1563 insertions, 8 deletions
diff --git a/framework/src/onos/apps/acl/pom.xml b/framework/src/onos/apps/acl/pom.xml index 54dee432..454ac7e6 100644 --- a/framework/src/onos/apps/acl/pom.xml +++ b/framework/src/onos/apps/acl/pom.xml @@ -18,7 +18,9 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> @@ -36,10 +38,10 @@ <url>http://onosproject.org</url> <properties> - <onos.version>1.4.0-SNAPSHOT</onos.version> <onos.app.name>org.onosproject.acl</onos.app.name> <onos.app.origin>DLUT</onos.app.origin> - <web.context>/onos/acl</web.context> + + <web.context>/onos/v1/acl</web.context> <api.version>1.0.0</api.version> <api.title>ONOS ACL Application REST API</api.title> <api.description> @@ -64,19 +66,34 @@ <dependency> <groupId>org.onosproject</groupId> <artifactId>onlab-junit</artifactId> - <version>${onos.version}</version> </dependency> <dependency> <groupId>org.onosproject</groupId> <artifactId>onos-rest</artifactId> - <version>${onos.version}</version> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onlab-osgi</artifactId> + <version>${project.version}</version> + <classifier>tests</classifier> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-rest</artifactId> + <version>${project.version}</version> + <classifier>tests</classifier> + <scope>test</scope> </dependency> <dependency> <groupId>org.onosproject</groupId> <artifactId>onos-core-serializers</artifactId> - <version>${onos.version}</version> + <version>${project.version}</version> </dependency> <dependency> @@ -100,7 +117,6 @@ <dependency> <groupId>org.onosproject</groupId> <artifactId>onlab-misc</artifactId> - <version>${onos.version}</version> </dependency> </dependencies> diff --git a/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclRule.java b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclRule.java new file mode 100644 index 00000000..8c91da4c --- /dev/null +++ b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclRule.java @@ -0,0 +1,290 @@ +/* + * 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; + +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(Ip4Prefix srcIp) { + this.srcIp = 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(Ip4Prefix dstIp) { + this.dstIp = 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. + * <p> + * 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/onosproject/acl/AclService.java b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclService.java new file mode 100644 index 00000000..487a6761 --- /dev/null +++ b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclService.java @@ -0,0 +1,56 @@ +/* + * 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; + +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<AclRule> 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/onosproject/acl/AclStore.java b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclStore.java new file mode 100644 index 00000000..ff9e25f6 --- /dev/null +++ b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclStore.java @@ -0,0 +1,146 @@ +/* + * 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; + +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<AclRule> 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<FlowRule> 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<RuleId> 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/onosproject/acl/AclWebResource.java b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclWebResource.java new file mode 100644 index 00000000..e792efba --- /dev/null +++ b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclWebResource.java @@ -0,0 +1,191 @@ +/* + * 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; + +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.onlab.packet.Ip4Prefix; +import org.onosproject.rest.AbstractWebResource; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +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.net.URI; +import java.net.URISyntaxException; +import java.util.List; + +/** + * Manage ACL rules. + */ +@Path("rules") +public class AclWebResource extends AbstractWebResource { + + /** + * Get all ACL rules. + * Returns array of all ACL rules. + * + * @return 200 OK + */ + @GET + public Response queryAclRule() { + List<AclRule> 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("aclRules", arrayNode); + return Response.ok(root.toString(), MediaType.APPLICATION_JSON_TYPE).build(); + } + + /** + * Add a new ACL rule. + * + * @param stream JSON data describing the rule + * @return 200 OK + */ + @POST + @Consumes(MediaType.APPLICATION_JSON) + public Response addAclRule(InputStream stream) throws URISyntaxException { + AclRule newRule = jsonToRule(stream); + return get(AclService.class).addAclRule(newRule) ? + Response.created(new URI(newRule.id().toString())).build() : + Response.serverError().build(); + } + + /** + * Remove ACL rule. + * + * @param id ACL rule id (in hex string format) + * @return 200 OK + */ + @DELETE + @Path("{id}") + public Response removeAclRule(@PathParam("id") String id) { + RuleId ruleId = new RuleId(Long.parseLong(id.substring(2), 16)); + get(AclService.class).removeAclRule(ruleId); + return Response.ok().build(); + } + + /** + * Remove all ACL rules. + * + * @return 200 OK + */ + @DELETE + public Response clearACL() { + get(AclService.class).clearAcl(); + return Response.ok().build(); + } + + /** + * Turns a JSON string into an ACL rule instance. + */ + private AclRule jsonToRule(InputStream stream) { + JsonNode node; + try { + node = mapper().readTree(stream); + } catch (IOException e) { + throw new IllegalArgumentException("Unable to parse ACL request", e); + } + + AclRule.Builder rule = AclRule.builder(); + + String s = node.path("srcIp").asText(null); + if (s != null) { + rule.srcIp(Ip4Prefix.valueOf(s)); + } + + s = node.path("dstIp").asText(null); + if (s != null) { + rule.dstIp(Ip4Prefix.valueOf(s)); + } + + s = node.path("ipProto").asText(null); + if (s != null) { + if ("TCP".equalsIgnoreCase(s)) { + rule.ipProto(IPv4.PROTOCOL_TCP); + } else if ("UDP".equalsIgnoreCase(s)) { + rule.ipProto(IPv4.PROTOCOL_UDP); + } else if ("ICMP".equalsIgnoreCase(s)) { + rule.ipProto(IPv4.PROTOCOL_ICMP); + } else { + throw new IllegalArgumentException("ipProto must be assigned to TCP, UDP, or ICMP"); + } + } + + int port = node.path("dstTpPort").asInt(0); + if (port > 0) { + rule.dstTpPort((short) port); + } + + s = node.path("action").asText(null); + if (s != null) { + if ("allow".equalsIgnoreCase(s)) { + rule.action(AclRule.Action.ALLOW); + } else if ("deny".equalsIgnoreCase(s)) { + rule.action(AclRule.Action.DENY); + } else { + throw new IllegalArgumentException("action must be ALLOW or DENY"); + } + } + + return rule.build(); + } + +}
\ No newline at end of file diff --git a/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/RuleId.java b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/RuleId.java new file mode 100644 index 00000000..468dab5c --- /dev/null +++ b/framework/src/onos/apps/acl/src/main/java/org/onosproject/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.onosproject.acl; + +/** + * ACL rule identifier suitable as an external key. + * <p>This class is immutable.</p> + */ +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/onosproject/acl/impl/AclManager.java b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/impl/AclManager.java new file mode 100644 index 00000000..f5c0c204 --- /dev/null +++ b/framework/src/onos/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/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/impl/DistributedAclStore.java b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/impl/DistributedAclStore.java new file mode 100644 index 00000000..a5fcfcc7 --- /dev/null +++ b/framework/src/onos/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/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/impl/package-info.java b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/impl/package-info.java new file mode 100644 index 00000000..9da9b3b7 --- /dev/null +++ b/framework/src/onos/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; diff --git a/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/package-info.java b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/package-info.java new file mode 100644 index 00000000..67f755c6 --- /dev/null +++ b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/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. + */ +package org.onosproject.acl; 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 index 2c2d5cf3..fc188b7f 100644 --- 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 @@ -33,7 +33,7 @@ </init-param> <init-param> <param-name>com.sun.jersey.config.property.classnames</param-name> - <param-value>org.onos.acl.AclWebResource</param-value> + <param-value>org.onosproject.acl.AclWebResource</param-value> </init-param> <load-on-startup>10</load-on-startup> </servlet> diff --git a/framework/src/onos/apps/acl/src/test/java/org/onosproject/acl/AclWebResourceTest.java b/framework/src/onos/apps/acl/src/test/java/org/onosproject/acl/AclWebResourceTest.java new file mode 100644 index 00000000..c554db6e --- /dev/null +++ b/framework/src/onos/apps/acl/src/test/java/org/onosproject/acl/AclWebResourceTest.java @@ -0,0 +1,142 @@ +/* + * 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.onosproject.acl; + +import com.sun.jersey.api.client.WebResource; +import com.sun.jersey.test.framework.AppDescriptor; +import com.sun.jersey.test.framework.WebAppDescriptor; +import org.junit.After; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.onlab.osgi.ServiceDirectory; +import org.onlab.osgi.TestServiceDirectory; +import org.onlab.rest.BaseResource; +import org.onosproject.core.IdGenerator; +import org.onosproject.rest.ResourceTest; + +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<AclRule> 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); + + IdGenerator idGenerator = new MockIdGenerator(); + AclRule.bindIdGenerator(idGenerator); + } + + @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(); + } + } + + @Override + public AppDescriptor configure() { + return new WebAppDescriptor.Builder("org.onosproject.acl").build(); + } + + @Test + @Ignore("FIXME: This needs to get reworked") + public void addRule() throws IOException { + WebResource.Builder rs = resource().path("rules").header("Content-type", "application/json"); + String response; + String json; + + replay(mockAclService); + + // input a invalid JSON string that contains neither nw_src and nw_dst + json = "{\"ipProto\":\"TCP\",\"dstTpPort\":\"80\"}"; + response = rs.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.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.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.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.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.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.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.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.post(String.class, json); + assertThat(response, containsString("action must be assigned to ALLOW or DENY.")); + } +} |