blob: d7e77485f84e31abfd799a4b748de06ee4c03718 [file] [log] [blame]
Pengfei Lue0c02e22015-07-07 15:41:31 +08001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
Pengfei Lue0c02e22015-07-07 15:41:31 +08003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
Ray Milkey85267002016-11-16 11:06:35 -080015 *
16 * Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China
17 * Advisers: Keqiu Li, Heng Qi and Haisheng Yu
18 * This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002)
19 * and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute.
Pengfei Lue0c02e22015-07-07 15:41:31 +080020 */
Thomas Vachuska9bb32352015-09-25 11:31:22 -070021package org.onosproject.acl.impl;
Pengfei Lue0c02e22015-07-07 15:41:31 +080022
Hyunsun Mooncf732fb2015-08-22 21:04:23 -070023import org.onlab.packet.Ethernet;
24import org.onlab.packet.IPv4;
25import org.onlab.packet.Ip4Address;
26import org.onlab.packet.Ip4Prefix;
27import org.onlab.packet.IpAddress;
28import org.onlab.packet.TpPort;
Thomas Vachuska9bb32352015-09-25 11:31:22 -070029import org.onosproject.acl.AclRule;
30import org.onosproject.acl.AclService;
31import org.onosproject.acl.AclStore;
Pengfei Lue0c02e22015-07-07 15:41:31 +080032import org.apache.felix.scr.annotations.Activate;
33import org.apache.felix.scr.annotations.Component;
34import org.apache.felix.scr.annotations.Deactivate;
35import org.apache.felix.scr.annotations.Reference;
36import org.apache.felix.scr.annotations.ReferenceCardinality;
37import org.apache.felix.scr.annotations.Service;
Thomas Vachuska9bb32352015-09-25 11:31:22 -070038import org.onosproject.acl.RuleId;
Pengfei Lue0c02e22015-07-07 15:41:31 +080039import org.onosproject.core.ApplicationId;
40import org.onosproject.core.CoreService;
41import org.onosproject.core.IdGenerator;
42import org.onosproject.mastership.MastershipService;
43import org.onosproject.net.DeviceId;
44import org.onosproject.net.Host;
45import org.onosproject.net.MastershipRole;
46import org.onosproject.net.PortNumber;
47import org.onosproject.net.flow.DefaultFlowEntry;
48import org.onosproject.net.flow.DefaultTrafficSelector;
49import org.onosproject.net.flow.DefaultTrafficTreatment;
50import org.onosproject.net.flow.FlowEntry;
51import org.onosproject.net.flow.FlowRule;
52import org.onosproject.net.flow.FlowRuleService;
53import org.onosproject.net.flow.TrafficSelector;
54import org.onosproject.net.flow.TrafficTreatment;
55import org.onosproject.net.flow.instructions.Instructions;
56import org.onosproject.net.host.HostEvent;
57import org.onosproject.net.host.HostListener;
58import org.onosproject.net.host.HostService;
59import org.slf4j.Logger;
60
61import java.util.HashSet;
62import java.util.List;
63import java.util.Set;
64
65import static org.slf4j.LoggerFactory.getLogger;
66
67/**
68 * Implementation of the ACL service.
69 */
70@Component(immediate = true)
71@Service
72public class AclManager implements AclService {
73
74 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
75 protected CoreService coreService;
76 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
77 protected FlowRuleService flowRuleService;
78 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
79 protected HostService hostService;
80 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
81 protected MastershipService mastershipService;
82
83 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
84 protected AclStore aclStore;
85
86 private final Logger log = getLogger(getClass());
87 private ApplicationId appId;
88 private final HostListener hostListener = new InternalHostListener();
89 private IdGenerator idGenerator;
90
91 /**
92 * Checks if the given IP address is in the given CIDR address.
93 */
Jonathan Hartd9df7bd2015-11-10 17:10:25 -080094 private boolean checkIpInCidr(Ip4Address ip, Ip4Prefix cidr) {
Pengfei Lue0c02e22015-07-07 15:41:31 +080095 int offset = 32 - cidr.prefixLength();
96 int cidrPrefix = cidr.address().toInt();
97 int ipIntValue = ip.toInt();
98 cidrPrefix = cidrPrefix >> offset;
99 ipIntValue = ipIntValue >> offset;
100 cidrPrefix = cidrPrefix << offset;
101 ipIntValue = ipIntValue << offset;
102
103 return (cidrPrefix == ipIntValue);
104 }
105
106 private class InternalHostListener implements HostListener {
107
108 /**
Andrea Campanella4b931a92018-06-22 17:28:41 +0200109 * Generate new ACL flow rules for new or updated host following the given ACL rule.
Pengfei Lue0c02e22015-07-07 15:41:31 +0800110 */
111 private void processHostAddedEvent(HostEvent event, AclRule rule) {
112 DeviceId deviceId = event.subject().location().deviceId();
113 for (IpAddress address : event.subject().ipAddresses()) {
114 if ((rule.srcIp() != null) ?
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800115 (checkIpInCidr(address.getIp4Address(), rule.srcIp())) :
116 (checkIpInCidr(address.getIp4Address(), rule.dstIp()))) {
Pengfei Lue0c02e22015-07-07 15:41:31 +0800117 if (!aclStore.checkIfRuleWorksInDevice(rule.id(), deviceId)) {
118 List<RuleId> allowingRuleList = aclStore
119 .getAllowingRuleByDenyingRule(rule.id());
120 if (allowingRuleList != null) {
121 for (RuleId allowingRuleId : allowingRuleList) {
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800122 generateAclFlow(aclStore.getAclRule(allowingRuleId), deviceId);
Pengfei Lue0c02e22015-07-07 15:41:31 +0800123 }
124 }
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800125 generateAclFlow(rule, deviceId);
Pengfei Lue0c02e22015-07-07 15:41:31 +0800126 }
127 }
128 }
129 }
130
131 @Override
132 public void event(HostEvent event) {
Andrea Campanella4b931a92018-06-22 17:28:41 +0200133 // if a new host appears or is updated and an existing rule denies
Pengfei Lue0c02e22015-07-07 15:41:31 +0800134 // its traffic, a new ACL flow rule is generated.
Andrea Campanella4b931a92018-06-22 17:28:41 +0200135 if (event.type() == HostEvent.Type.HOST_ADDED || event.type() == HostEvent.Type.HOST_UPDATED) {
Pengfei Lue0c02e22015-07-07 15:41:31 +0800136 DeviceId deviceId = event.subject().location().deviceId();
137 if (mastershipService.getLocalRole(deviceId) == MastershipRole.MASTER) {
138 for (AclRule rule : aclStore.getAclRules()) {
139 if (rule.action() != AclRule.Action.ALLOW) {
140 processHostAddedEvent(event, rule);
141 }
142 }
143 }
144 }
145 }
146 }
147
148 @Activate
149 public void activate() {
Thomas Vachuska1b1537c2015-12-17 09:06:52 -0800150 appId = coreService.registerApplication("org.onosproject.acl");
Pengfei Lue0c02e22015-07-07 15:41:31 +0800151 hostService.addListener(hostListener);
152 idGenerator = coreService.getIdGenerator("acl-ids");
153 AclRule.bindIdGenerator(idGenerator);
154 log.info("Started");
155 }
156
157 @Deactivate
158 public void deactivate() {
159 hostService.removeListener(hostListener);
sangyun-hanfd286c92017-01-26 16:24:44 +0900160 this.clearAcl();
Pengfei Lue0c02e22015-07-07 15:41:31 +0800161 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
Nitin Anandfb22901d2018-05-09 12:16:36 +0530268 if (rule.srcMac() != null) {
269 selectorBuilder.matchEthSrc(rule.srcMac());
270
271 }
272 if (rule.dstMac() != null) {
273 selectorBuilder.matchEthDst(rule.dstMac());
274 }
275
Pengfei Lue0c02e22015-07-07 15:41:31 +0800276 selectorBuilder.matchEthType(Ethernet.TYPE_IPV4);
277 if (rule.srcIp() != null) {
278 selectorBuilder.matchIPSrc(rule.srcIp());
279 if (rule.dstIp() != null) {
280 selectorBuilder.matchIPDst(rule.dstIp());
281 }
282 } else {
283 selectorBuilder.matchIPDst(rule.dstIp());
284 }
285 if (rule.ipProto() != 0) {
286 selectorBuilder.matchIPProtocol(Integer.valueOf(rule.ipProto()).byteValue());
287 }
Nitin Anandfb22901d2018-05-09 12:16:36 +0530288 if (rule.dscp() != 0) {
289 selectorBuilder.matchIPDscp(Byte.valueOf(rule.dscp()));
290 }
Pengfei Lue0c02e22015-07-07 15:41:31 +0800291 if (rule.dstTpPort() != 0) {
292 switch (rule.ipProto()) {
293 case IPv4.PROTOCOL_TCP:
Hyunsun Mooncf732fb2015-08-22 21:04:23 -0700294 selectorBuilder.matchTcpDst(TpPort.tpPort(rule.dstTpPort()));
Pengfei Lue0c02e22015-07-07 15:41:31 +0800295 break;
296 case IPv4.PROTOCOL_UDP:
Hyunsun Mooncf732fb2015-08-22 21:04:23 -0700297 selectorBuilder.matchUdpDst(TpPort.tpPort(rule.dstTpPort()));
Pengfei Lue0c02e22015-07-07 15:41:31 +0800298 break;
299 default:
300 break;
301 }
302 }
Nitin Anandfb22901d2018-05-09 12:16:36 +0530303
304 if (rule.srcTpPort() != 0) {
305 switch (rule.ipProto()) {
306 case IPv4.PROTOCOL_TCP:
307 selectorBuilder.matchTcpSrc(TpPort.tpPort(rule.srcTpPort()));
308 break;
309 case IPv4.PROTOCOL_UDP:
310 selectorBuilder.matchUdpSrc(TpPort.tpPort(rule.srcTpPort()));
311 break;
312 default:
313 break;
314 }
315 }
316
Pengfei Lue0c02e22015-07-07 15:41:31 +0800317 if (rule.action() == AclRule.Action.ALLOW) {
318 treatment.add(Instructions.createOutput(PortNumber.CONTROLLER));
319 }
320 flowEntry.forDevice(deviceId);
321 flowEntry.withPriority(aclStore.getPriorityByDevice(deviceId));
322 flowEntry.withSelector(selectorBuilder.build());
323 flowEntry.withTreatment(treatment.build());
324 flowEntry.fromApp(appId);
325 flowEntry.makePermanent();
326 // install flow rule
327 flowRuleService.applyFlowRules(flowEntry.build());
328 log.debug("ACL flow rule {} is installed in {}.", flowEntry.build(), deviceId);
329 aclStore.addRuleToFlowMapping(rule.id(), flowEntry.build());
330 aclStore.addRuleToDeviceMapping(rule.id(), deviceId);
331 }
332
333 @Override
334 public void removeAclRule(RuleId ruleId) {
335 aclStore.removeAclRule(ruleId);
336 log.info("ACL rule(id:{}) is removed.", ruleId);
337 enforceRuleRemoving(ruleId);
338 }
339
340 /**
341 * Enforces removing an existing ACL rule.
342 */
343 private void enforceRuleRemoving(RuleId ruleId) {
344 Set<FlowRule> flowSet = aclStore.getFlowByRule(ruleId);
345 if (flowSet != null) {
346 for (FlowRule flowRule : flowSet) {
347 flowRuleService.removeFlowRules(flowRule);
348 log.debug("ACL flow rule {} is removed from {}.", flowRule.toString(), flowRule.deviceId().toString());
349 }
350 }
351 aclStore.removeRuleToFlowMapping(ruleId);
352 aclStore.removeRuleToDeviceMapping(ruleId);
353 aclStore.removeDenyToAllowMapping(ruleId);
354 }
355
356 @Override
357 public void clearAcl() {
358 aclStore.clearAcl();
359 flowRuleService.removeFlowRulesById(appId);
360 log.info("ACL is cleared.");
361 }
362
363}