| /* |
| * Copyright 2015 Open Networking Laboratory |
| * Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China |
| * Advisers: Keqiu Li, Heng Qi and Haisheng Yu |
| * This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002) |
| * and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.onos.acl; |
| |
| import com.fasterxml.jackson.core.JsonParser; |
| import com.fasterxml.jackson.core.JsonToken; |
| import com.fasterxml.jackson.databind.JsonNode; |
| import com.fasterxml.jackson.databind.ObjectMapper; |
| import com.fasterxml.jackson.databind.node.ArrayNode; |
| import com.fasterxml.jackson.databind.node.ObjectNode; |
| import org.onlab.packet.IPv4; |
| import org.onosproject.rest.AbstractWebResource; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import javax.ws.rs.GET; |
| import javax.ws.rs.POST; |
| import javax.ws.rs.Path; |
| import javax.ws.rs.PathParam; |
| import javax.ws.rs.core.MediaType; |
| import javax.ws.rs.core.Response; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.util.List; |
| |
| // FIXME: This does now follow REST-full principles and should be refactored. |
| /** |
| * Manage ACL rules. |
| */ |
| @Path("") |
| public class AclWebResource extends AbstractWebResource { |
| |
| private final Logger log = LoggerFactory.getLogger(getClass()); |
| |
| /** |
| * Processes user's GET HTTP request for querying ACL rules. |
| * @return response to the request |
| */ |
| @GET |
| public Response queryAclRule() { |
| List<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(); |
| } |
| |
| } |