blob: 2ef8041017d6b8f2da4dca6d1d8f75cdb41ae566 [file] [log] [blame]
Pengfei Lue0c02e22015-07-07 15:41:31 +08001/*
2 * Copyright 2015 Open Networking Laboratory
3 * Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China
4 * Advisers: Keqiu Li and Heng Qi
5 * This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002)
6 * and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute.
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20package org.onos.acl;
21
22import com.fasterxml.jackson.core.JsonParser;
23import com.fasterxml.jackson.core.JsonToken;
24import com.fasterxml.jackson.databind.JsonNode;
25import com.fasterxml.jackson.databind.ObjectMapper;
26import com.fasterxml.jackson.databind.node.ArrayNode;
27import com.fasterxml.jackson.databind.node.ObjectNode;
28import org.onlab.packet.IPv4;
29import org.onosproject.rest.AbstractWebResource;
30import org.slf4j.Logger;
31import org.slf4j.LoggerFactory;
32
33import javax.ws.rs.GET;
34import javax.ws.rs.POST;
35import javax.ws.rs.Path;
36import javax.ws.rs.PathParam;
37import javax.ws.rs.core.MediaType;
38import javax.ws.rs.core.Response;
39import java.io.IOException;
40import java.io.InputStream;
41import java.util.List;
42
43/**
44 * REST resource for interacting with ACL application.
45 */
46@Path("")
47public class AclWebResource extends AbstractWebResource {
48
49 private final Logger log = LoggerFactory.getLogger(getClass());
50
51 /**
52 * Processes user's GET HTTP request for querying ACL rules.
53 * @return response to the request
54 */
55 @GET
56 public Response queryAclRule() {
57 List<AclRule> rules = get(AclService.class).getAclRules();
58 ObjectMapper mapper = new ObjectMapper();
59 ObjectNode root = mapper.createObjectNode();
60 ArrayNode arrayNode = mapper.createArrayNode();
61 for (AclRule rule : rules) {
62 ObjectNode node = mapper.createObjectNode();
63 node.put("id", rule.id().toString());
64 if (rule.srcIp() != null) {
65 node.put("srcIp", rule.srcIp().toString());
66 }
67 if (rule.dstIp() != null) {
68 node.put("dstIp", rule.dstIp().toString());
69 }
70 if (rule.ipProto() != 0) {
71 switch (rule.ipProto()) {
72 case IPv4.PROTOCOL_ICMP:
73 node.put("ipProto", "ICMP");
74 break;
75 case IPv4.PROTOCOL_TCP:
76 node.put("ipProto", "TCP");
77 break;
78 case IPv4.PROTOCOL_UDP:
79 node.put("ipProto", "UDP");
80 break;
81 default:
82 break;
83 }
84 }
85 if (rule.dstTpPort() != 0) {
86 node.put("dstTpPort", rule.dstTpPort());
87 }
88 node.put("action", rule.action().toString());
89 arrayNode.add(node);
90 }
91 root.set("ACL rules", arrayNode);
92 return Response.ok(root.toString(), MediaType.APPLICATION_JSON_TYPE).build();
93 }
94
95 /**
96 * Processes user's POST HTTP request for add ACL rules.
97 * @param stream input stream
98 * @return response to the request
99 */
100 @POST
101 @Path("add")
102 public Response addAclRule(InputStream stream) {
103 AclRule newRule;
104 try {
105 newRule = jsonToRule(stream);
106 } catch (Exception e) {
107 return Response.ok("{\"status\" : \"Failed! " + e.getMessage() + "\"}").build();
108 }
109
110 String status;
111 if (get(AclService.class).addAclRule(newRule)) {
112 status = "Success! New ACL rule is added.";
113 } else {
114 status = "Failed! New ACL rule matches an existing rule.";
115 }
116 return Response.ok("{\"status\" : \"" + status + "\"}").build();
117 }
118
119 /**
120 * Processes user's GET HTTP request for removing ACL rule.
121 * @param id ACL rule id (in hex string format)
122 * @return response to the request
123 */
124 @GET
125 @Path("remove/{id}")
126 public Response removeAclRule(@PathParam("id") String id) {
127 String status;
128 RuleId ruleId = new RuleId(Long.parseLong(id.substring(2), 16));
129 if (get(AclStore.class).getAclRule(ruleId) == null) {
130 status = "Failed! There is no ACL rule with this id.";
131 } else {
132 get(AclService.class).removeAclRule(ruleId);
133 status = "Success! ACL rule(id:" + id + ") is removed.";
134 }
135 return Response.ok("{\"status\" : \"" + status + "\"}").build();
136 }
137
138 /**
139 * Processes user's GET HTTP request for clearing ACL.
140 * @return response to the request
141 */
142 @GET
143 @Path("clear")
144 public Response clearACL() {
145 get(AclService.class).clearAcl();
146 return Response.ok("{\"status\" : \"ACL is cleared.\"}").build();
147 }
148
149 /**
150 * Exception class for parsing a invalid ACL rule.
151 */
152 private class AclRuleParseException extends Exception {
153 public AclRuleParseException(String message) {
154 super(message);
155 }
156 }
157
158 /**
159 * Turns a JSON string into an ACL rule instance.
160 */
161 private AclRule jsonToRule(InputStream stream) throws AclRuleParseException, IOException {
162 ObjectMapper mapper = new ObjectMapper();
163 JsonNode jsonNode = mapper.readTree(stream);
164 JsonParser jp = jsonNode.traverse();
165 AclRule.Builder rule = AclRule.builder();
166 jp.nextToken();
167 if (jp.getCurrentToken() != JsonToken.START_OBJECT) {
168 throw new AclRuleParseException("Expected START_OBJECT");
169 }
170
171 while (jp.nextToken() != JsonToken.END_OBJECT) {
172 if (jp.getCurrentToken() != JsonToken.FIELD_NAME) {
173 throw new AclRuleParseException("Expected FIELD_NAME");
174 }
175
176 String key = jp.getCurrentName();
177 jp.nextToken();
178 String value = jp.getText();
179 if ("".equals(value)) {
180 continue;
181 }
182
183 if ("srcIp".equals(key)) {
184 rule.srcIp(value);
185 } else if ("dstIp".equals(key)) {
186 rule.dstIp(value);
187 } else if ("ipProto".equals(key)) {
188 if ("TCP".equalsIgnoreCase(value)) {
189 rule.ipProto(IPv4.PROTOCOL_TCP);
190 } else if ("UDP".equalsIgnoreCase(value)) {
191 rule.ipProto(IPv4.PROTOCOL_UDP);
192 } else if ("ICMP".equalsIgnoreCase(value)) {
193 rule.ipProto(IPv4.PROTOCOL_ICMP);
194 } else {
195 throw new AclRuleParseException("ipProto must be assigned to TCP, UDP, or ICMP.");
196 }
197 } else if ("dstTpPort".equals(key)) {
198 try {
199 rule.dstTpPort(Short.parseShort(value));
200 } catch (NumberFormatException e) {
201 throw new AclRuleParseException("dstTpPort must be assigned to a numerical value.");
202 }
203 } else if ("action".equals(key)) {
204 if (!"allow".equalsIgnoreCase(value) && !"deny".equalsIgnoreCase(value)) {
205 throw new AclRuleParseException("action must be assigned to ALLOW or DENY.");
206 }
207 if ("allow".equalsIgnoreCase(value)) {
208 rule.action(AclRule.Action.ALLOW);
209 }
210 }
211 }
212 return rule.build();
213 }
214
215}