blob: 638f07997f32aa52cee561c4c18592fad74e9c28 [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
Pengfei Lue53419b2015-08-29 10:11:02 +08004 * Advisers: Keqiu Li, Heng Qi and Haisheng Yu
Pengfei Lue0c02e22015-07-07 15:41:31 +08005 * 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 */
Thomas Vachuska9bb32352015-09-25 11:31:22 -070020package org.onosproject.acl.impl;
Pengfei Lue0c02e22015-07-07 15:41:31 +080021
Hyunsun Mooncf732fb2015-08-22 21:04:23 -070022import org.onlab.packet.Ethernet;
23import org.onlab.packet.IPv4;
24import org.onlab.packet.Ip4Address;
25import org.onlab.packet.Ip4Prefix;
26import org.onlab.packet.IpAddress;
27import org.onlab.packet.TpPort;
Thomas Vachuska9bb32352015-09-25 11:31:22 -070028import org.onosproject.acl.AclRule;
29import org.onosproject.acl.AclService;
30import org.onosproject.acl.AclStore;
Pengfei Lue0c02e22015-07-07 15:41:31 +080031import org.apache.felix.scr.annotations.Activate;
32import org.apache.felix.scr.annotations.Component;
33import org.apache.felix.scr.annotations.Deactivate;
34import org.apache.felix.scr.annotations.Reference;
35import org.apache.felix.scr.annotations.ReferenceCardinality;
36import org.apache.felix.scr.annotations.Service;
Thomas Vachuska9bb32352015-09-25 11:31:22 -070037import org.onosproject.acl.RuleId;
Pengfei Lue0c02e22015-07-07 15:41:31 +080038import org.onosproject.core.ApplicationId;
39import org.onosproject.core.CoreService;
40import org.onosproject.core.IdGenerator;
41import org.onosproject.mastership.MastershipService;
42import org.onosproject.net.DeviceId;
43import org.onosproject.net.Host;
44import org.onosproject.net.MastershipRole;
45import org.onosproject.net.PortNumber;
46import org.onosproject.net.flow.DefaultFlowEntry;
47import org.onosproject.net.flow.DefaultTrafficSelector;
48import org.onosproject.net.flow.DefaultTrafficTreatment;
49import org.onosproject.net.flow.FlowEntry;
50import org.onosproject.net.flow.FlowRule;
51import org.onosproject.net.flow.FlowRuleService;
52import org.onosproject.net.flow.TrafficSelector;
53import org.onosproject.net.flow.TrafficTreatment;
54import org.onosproject.net.flow.instructions.Instructions;
55import org.onosproject.net.host.HostEvent;
56import org.onosproject.net.host.HostListener;
57import org.onosproject.net.host.HostService;
58import org.slf4j.Logger;
59
60import java.util.HashSet;
61import java.util.List;
62import java.util.Set;
63
64import static org.slf4j.LoggerFactory.getLogger;
65
66/**
67 * Implementation of the ACL service.
68 */
69@Component(immediate = true)
70@Service
71public class AclManager implements AclService {
72
73 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
74 protected CoreService coreService;
75 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
76 protected FlowRuleService flowRuleService;
77 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
78 protected HostService hostService;
79 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
80 protected MastershipService mastershipService;
81
82 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
83 protected AclStore aclStore;
84
85 private final Logger log = getLogger(getClass());
86 private ApplicationId appId;
87 private final HostListener hostListener = new InternalHostListener();
88 private IdGenerator idGenerator;
89
90 /**
91 * Checks if the given IP address is in the given CIDR address.
92 */
Jonathan Hartd9df7bd2015-11-10 17:10:25 -080093 private boolean checkIpInCidr(Ip4Address ip, Ip4Prefix cidr) {
Pengfei Lue0c02e22015-07-07 15:41:31 +080094 int offset = 32 - cidr.prefixLength();
95 int cidrPrefix = cidr.address().toInt();
96 int ipIntValue = ip.toInt();
97 cidrPrefix = cidrPrefix >> offset;
98 ipIntValue = ipIntValue >> offset;
99 cidrPrefix = cidrPrefix << offset;
100 ipIntValue = ipIntValue << offset;
101
102 return (cidrPrefix == ipIntValue);
103 }
104
105 private class InternalHostListener implements HostListener {
106
107 /**
108 * Generate new ACL flow rules for new host following the given ACL rule.
109 */
110 private void processHostAddedEvent(HostEvent event, AclRule rule) {
111 DeviceId deviceId = event.subject().location().deviceId();
112 for (IpAddress address : event.subject().ipAddresses()) {
113 if ((rule.srcIp() != null) ?
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800114 (checkIpInCidr(address.getIp4Address(), rule.srcIp())) :
115 (checkIpInCidr(address.getIp4Address(), rule.dstIp()))) {
Pengfei Lue0c02e22015-07-07 15:41:31 +0800116 if (!aclStore.checkIfRuleWorksInDevice(rule.id(), deviceId)) {
117 List<RuleId> allowingRuleList = aclStore
118 .getAllowingRuleByDenyingRule(rule.id());
119 if (allowingRuleList != null) {
120 for (RuleId allowingRuleId : allowingRuleList) {
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800121 generateAclFlow(aclStore.getAclRule(allowingRuleId), deviceId);
Pengfei Lue0c02e22015-07-07 15:41:31 +0800122 }
123 }
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800124 generateAclFlow(rule, deviceId);
Pengfei Lue0c02e22015-07-07 15:41:31 +0800125 }
126 }
127 }
128 }
129
130 @Override
131 public void event(HostEvent event) {
132 // if a new host appears and an existing rule denies
133 // its traffic, a new ACL flow rule is generated.
134 if (event.type() == HostEvent.Type.HOST_ADDED) {
135 DeviceId deviceId = event.subject().location().deviceId();
136 if (mastershipService.getLocalRole(deviceId) == MastershipRole.MASTER) {
137 for (AclRule rule : aclStore.getAclRules()) {
138 if (rule.action() != AclRule.Action.ALLOW) {
139 processHostAddedEvent(event, rule);
140 }
141 }
142 }
143 }
144 }
145 }
146
147 @Activate
148 public void activate() {
149 appId = coreService.registerApplication("org.onos.acl");
150 hostService.addListener(hostListener);
151 idGenerator = coreService.getIdGenerator("acl-ids");
152 AclRule.bindIdGenerator(idGenerator);
153 log.info("Started");
154 }
155
156 @Deactivate
157 public void deactivate() {
158 hostService.removeListener(hostListener);
159 flowRuleService.removeFlowRulesById(appId);
160 aclStore.clearAcl();
161 log.info("Stopped");
162 }
163
164 @Override
165 public List<AclRule> getAclRules() {
166 return aclStore.getAclRules();
167 }
168
169 /**
170 * Checks if the new ACL rule matches an existing rule.
171 * If existing allowing rules matches the new denying rule, store the mappings.
Thomas Vachuska9bb32352015-09-25 11:31:22 -0700172 *
Pengfei Lue0c02e22015-07-07 15:41:31 +0800173 * @return true if the new ACL rule matches an existing rule, false otherwise
174 */
175 private boolean matchCheck(AclRule newRule) {
176 for (AclRule existingRule : aclStore.getAclRules()) {
177 if (newRule.checkMatch(existingRule)) {
178 return true;
179 }
180
181 if (existingRule.action() == AclRule.Action.ALLOW
182 && newRule.action() == AclRule.Action.DENY) {
183 if (existingRule.checkMatch(newRule)) {
184 aclStore.addDenyToAllowMapping(newRule.id(), existingRule.id());
185 }
186 }
187 }
188 return false;
189 }
190
191 @Override
192 public boolean addAclRule(AclRule rule) {
193 if (matchCheck(rule)) {
194 return false;
195 }
196 aclStore.addAclRule(rule);
197 log.info("ACL rule(id:{}) is added.", rule.id());
198 if (rule.action() != AclRule.Action.ALLOW) {
199 enforceRuleAdding(rule);
200 }
201 return true;
202 }
203
204 /**
205 * Gets a set containing all devices connecting with the hosts
206 * whose IP address is in the given CIDR IP address.
207 */
208 private Set<DeviceId> getDeviceIdSet(Ip4Prefix cidrAddr) {
209 Set<DeviceId> deviceIdSet = new HashSet<>();
210 final Iterable<Host> hosts = hostService.getHosts();
211
212 if (cidrAddr.prefixLength() != 32) {
213 for (Host h : hosts) {
214 for (IpAddress a : h.ipAddresses()) {
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800215 if (checkIpInCidr(a.getIp4Address(), cidrAddr)) {
Pengfei Lue0c02e22015-07-07 15:41:31 +0800216 deviceIdSet.add(h.location().deviceId());
217 }
218 }
219 }
220 } else {
221 for (Host h : hosts) {
222 for (IpAddress a : h.ipAddresses()) {
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800223 if (checkIpInCidr(a.getIp4Address(), cidrAddr)) {
Pengfei Lue0c02e22015-07-07 15:41:31 +0800224 deviceIdSet.add(h.location().deviceId());
225 return deviceIdSet;
226 }
227 }
228 }
229 }
230 return deviceIdSet;
231 }
232
233 /**
234 * Enforces denying ACL rule by ACL flow rules.
235 */
236 private void enforceRuleAdding(AclRule rule) {
237 Set<DeviceId> dpidSet;
238 if (rule.srcIp() != null) {
239 dpidSet = getDeviceIdSet(rule.srcIp());
240 } else {
241 dpidSet = getDeviceIdSet(rule.dstIp());
242 }
243
244 for (DeviceId deviceId : dpidSet) {
245 List<RuleId> allowingRuleList = aclStore.getAllowingRuleByDenyingRule(rule.id());
246 if (allowingRuleList != null) {
247 for (RuleId allowingRuleId : allowingRuleList) {
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800248 generateAclFlow(aclStore.getAclRule(allowingRuleId), deviceId);
Pengfei Lue0c02e22015-07-07 15:41:31 +0800249 }
250 }
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800251 generateAclFlow(rule, deviceId);
Pengfei Lue0c02e22015-07-07 15:41:31 +0800252 }
253 }
254
255 /**
256 * Generates ACL flow rule according to ACL rule
257 * and install it into related device.
258 */
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800259 private void generateAclFlow(AclRule rule, DeviceId deviceId) {
Pengfei Lue0c02e22015-07-07 15:41:31 +0800260 if (rule == null || aclStore.checkIfRuleWorksInDevice(rule.id(), deviceId)) {
261 return;
262 }
263
264 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
265 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
266 FlowEntry.Builder flowEntry = DefaultFlowEntry.builder();
267
268 selectorBuilder.matchEthType(Ethernet.TYPE_IPV4);
269 if (rule.srcIp() != null) {
270 selectorBuilder.matchIPSrc(rule.srcIp());
271 if (rule.dstIp() != null) {
272 selectorBuilder.matchIPDst(rule.dstIp());
273 }
274 } else {
275 selectorBuilder.matchIPDst(rule.dstIp());
276 }
277 if (rule.ipProto() != 0) {
278 selectorBuilder.matchIPProtocol(Integer.valueOf(rule.ipProto()).byteValue());
279 }
280 if (rule.dstTpPort() != 0) {
281 switch (rule.ipProto()) {
282 case IPv4.PROTOCOL_TCP:
Hyunsun Mooncf732fb2015-08-22 21:04:23 -0700283 selectorBuilder.matchTcpDst(TpPort.tpPort(rule.dstTpPort()));
Pengfei Lue0c02e22015-07-07 15:41:31 +0800284 break;
285 case IPv4.PROTOCOL_UDP:
Hyunsun Mooncf732fb2015-08-22 21:04:23 -0700286 selectorBuilder.matchUdpDst(TpPort.tpPort(rule.dstTpPort()));
Pengfei Lue0c02e22015-07-07 15:41:31 +0800287 break;
288 default:
289 break;
290 }
291 }
292 if (rule.action() == AclRule.Action.ALLOW) {
293 treatment.add(Instructions.createOutput(PortNumber.CONTROLLER));
294 }
295 flowEntry.forDevice(deviceId);
296 flowEntry.withPriority(aclStore.getPriorityByDevice(deviceId));
297 flowEntry.withSelector(selectorBuilder.build());
298 flowEntry.withTreatment(treatment.build());
299 flowEntry.fromApp(appId);
300 flowEntry.makePermanent();
301 // install flow rule
302 flowRuleService.applyFlowRules(flowEntry.build());
303 log.debug("ACL flow rule {} is installed in {}.", flowEntry.build(), deviceId);
304 aclStore.addRuleToFlowMapping(rule.id(), flowEntry.build());
305 aclStore.addRuleToDeviceMapping(rule.id(), deviceId);
306 }
307
308 @Override
309 public void removeAclRule(RuleId ruleId) {
310 aclStore.removeAclRule(ruleId);
311 log.info("ACL rule(id:{}) is removed.", ruleId);
312 enforceRuleRemoving(ruleId);
313 }
314
315 /**
316 * Enforces removing an existing ACL rule.
317 */
318 private void enforceRuleRemoving(RuleId ruleId) {
319 Set<FlowRule> flowSet = aclStore.getFlowByRule(ruleId);
320 if (flowSet != null) {
321 for (FlowRule flowRule : flowSet) {
322 flowRuleService.removeFlowRules(flowRule);
323 log.debug("ACL flow rule {} is removed from {}.", flowRule.toString(), flowRule.deviceId().toString());
324 }
325 }
326 aclStore.removeRuleToFlowMapping(ruleId);
327 aclStore.removeRuleToDeviceMapping(ruleId);
328 aclStore.removeDenyToAllowMapping(ruleId);
329 }
330
331 @Override
332 public void clearAcl() {
333 aclStore.clearAcl();
334 flowRuleService.removeFlowRulesById(appId);
335 log.info("ACL is cleared.");
336 }
337
338}