Pengfei Lu
Committed by Gerrit Code Review

Add an ACL application

- change both of the constructors in AclRule.class to be private
- change AclRule.class to be final
- remove useless reference

|URL|Notes|
|-|-|
|GET onos/v1/acl | Lists all existing ACL rules.|
|GET onos/v1/acl/remove/{id} | Removes an existing ACL rule by id|
|GET onos/v1/acl/clear | Clears ACL and reset all|
|POST onos/v1/acl/add | Adds a new ACL rule|

|Key|Value|Notes|
|-|-|-|
|ipProto | string | "TCP" or "UDP" or "ICMP" (ignoring case)|
|srcIp | IPv4 address[/mask] | Either src-ip or dst-ip must be specified.|
|dstIp | IPv4 address[/mask] | Either src-ip or dst-ip must be specified.|
|dstTpPort | number | Valid when nw-proto == "TCP" or "UDP".|
|action | string | "DENY" or "ALLOW" (ignoring case), set to "DENY" if not specified.|

Change-Id: I55170d5f50814eabef43b1bf2ee33af41b5987e4
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
-->
<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>
<groupId>org.onosproject</groupId>
<artifactId>onos-apps</artifactId>
<version>1.3.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>onos-app-acl</artifactId>
<version>1.3.0-SNAPSHOT</version>
<packaging>bundle</packaging>
<description>ONOS ACL application</description>
<url>http://onosproject.org</url>
<properties>
<onos.version>1.3.0-SNAPSHOT</onos.version>
<onos.app.name>org.onosproject.acl</onos.app.name>
<onos.app.origin>Pengfei Lu</onos.app.origin>
<web.context>/onos/v1/acl</web.context>
</properties>
<dependencies>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>jsr311-api</artifactId>
<version>1.1.1</version>
</dependency>
<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>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-core-serializers</artifactId>
<version>${onos.version}</version>
</dependency>
<dependency>
<groupId>com.sun.jersey.jersey-test-framework</groupId>
<artifactId>jersey-test-framework-core</artifactId>
<version>1.19</version>
</dependency>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<version>3.2</version>
</dependency>
<dependency>
<groupId>com.sun.jersey.jersey-test-framework</groupId>
<artifactId>jersey-test-framework-grizzly2</artifactId>
<version>1.19</version>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onlab-misc</artifactId>
<version>${onos.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>2.5.3</version>
<extensions>true</extensions>
<configuration>
<instructions>
<_wab>src/main/webapp/</_wab>
<Bundle-SymbolicName>
${project.groupId}.${project.artifactId}
</Bundle-SymbolicName>
<Import-Package>
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.*;
</Import-Package>
<Web-ContextPath>${web.context}</Web-ContextPath>
</instructions>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.5.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-scr-plugin</artifactId>
<version>1.20.0</version>
<executions>
<execution>
<id>generate-scr-srcdescriptor</id>
<goals>
<goal>scr</goal>
</goals>
</execution>
</executions>
<configuration>
<supportedProjectTypes>
<supportedProjectType>bundle</supportedProjectType>
<supportedProjectType>war</supportedProjectType>
</supportedProjectTypes>
</configuration>
</plugin>
<plugin>
<groupId>org.onosproject</groupId>
<artifactId>onos-maven-plugin</artifactId>
<version>1.4-SNAPSHOT</version>
<executions>
<execution>
<id>cfg</id>
<phase>generate-resources</phase>
<goals>
<goal>cfg</goal>
</goals>
</execution>
<execution>
<id>app</id>
<phase>package</phase>
<goals>
<goal>app</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
/*
* 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;
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();
}
}
/*
* 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;
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
/*
* 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;
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);
}
/*
* 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;
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;
/**
* REST resource for interacting with ACL application.
*/
@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<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("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
/*
* 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.
* <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);
}
}
This diff is collapsed. Click to expand it.
/*
* 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.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<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<DeviceId>();
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
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
-->
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="ONOS" version="2.5">
<display-name>ACL application</display-name>
<servlet>
<servlet-name>JAX-RS Service</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.resourceConfigClass</param-name>
<param-value>com.sun.jersey.api.core.ClassNamesResourceConfig</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.config.property.classnames</param-name>
<param-value>org.onos.acl.AclWebResource</param-value>
</init-param>
<load-on-startup>10</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>JAX-RS Service</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
/*
* 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<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);
}
@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."));
}
}
/*
* 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();
}
}
/*
* 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<Object> services = MutableClassToInstanceMap.create();
@Override
public <T> T get(Class<T> 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;
}
}
......@@ -33,6 +33,7 @@
<modules>
<module>aaa</module>
<module>acl</module>
<module>fwd</module>
<module>mobility</module>
<module>proxyarp</module>
......