blob: 8b396618c7510c2bba0cf469cd40bc803910e7e7 [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;
Thomas Vachuska9bb32352015-09-25 11:31:22 -070032import org.onosproject.acl.RuleId;
Pengfei Lue0c02e22015-07-07 15:41:31 +080033import org.onosproject.core.ApplicationId;
34import org.onosproject.core.CoreService;
35import org.onosproject.core.IdGenerator;
36import org.onosproject.mastership.MastershipService;
37import org.onosproject.net.DeviceId;
38import org.onosproject.net.Host;
39import org.onosproject.net.MastershipRole;
40import org.onosproject.net.PortNumber;
41import org.onosproject.net.flow.DefaultFlowEntry;
42import org.onosproject.net.flow.DefaultTrafficSelector;
43import org.onosproject.net.flow.DefaultTrafficTreatment;
44import org.onosproject.net.flow.FlowEntry;
45import org.onosproject.net.flow.FlowRule;
46import org.onosproject.net.flow.FlowRuleService;
47import org.onosproject.net.flow.TrafficSelector;
48import org.onosproject.net.flow.TrafficTreatment;
49import org.onosproject.net.flow.instructions.Instructions;
50import org.onosproject.net.host.HostEvent;
51import org.onosproject.net.host.HostListener;
52import org.onosproject.net.host.HostService;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070053import org.osgi.service.component.annotations.Activate;
54import org.osgi.service.component.annotations.Component;
55import org.osgi.service.component.annotations.Deactivate;
56import org.osgi.service.component.annotations.Reference;
57import org.osgi.service.component.annotations.ReferenceCardinality;
Pengfei Lue0c02e22015-07-07 15:41:31 +080058import 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 */
Ray Milkeyd84f89b2018-08-17 14:54:17 -070069@Component(immediate = true, service = AclService.class)
Pengfei Lue0c02e22015-07-07 15:41:31 +080070public class AclManager implements AclService {
71
Ray Milkeyd84f89b2018-08-17 14:54:17 -070072 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Pengfei Lue0c02e22015-07-07 15:41:31 +080073 protected CoreService coreService;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070074 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Pengfei Lue0c02e22015-07-07 15:41:31 +080075 protected FlowRuleService flowRuleService;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070076 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Pengfei Lue0c02e22015-07-07 15:41:31 +080077 protected HostService hostService;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070078 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Pengfei Lue0c02e22015-07-07 15:41:31 +080079 protected MastershipService mastershipService;
80
Ray Milkeyd84f89b2018-08-17 14:54:17 -070081 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Pengfei Lue0c02e22015-07-07 15:41:31 +080082 protected AclStore aclStore;
83
84 private final Logger log = getLogger(getClass());
85 private ApplicationId appId;
86 private final HostListener hostListener = new InternalHostListener();
87 private IdGenerator idGenerator;
88
89 /**
90 * Checks if the given IP address is in the given CIDR address.
91 */
Jonathan Hartd9df7bd2015-11-10 17:10:25 -080092 private boolean checkIpInCidr(Ip4Address ip, Ip4Prefix cidr) {
Pengfei Lue0c02e22015-07-07 15:41:31 +080093 int offset = 32 - cidr.prefixLength();
94 int cidrPrefix = cidr.address().toInt();
95 int ipIntValue = ip.toInt();
96 cidrPrefix = cidrPrefix >> offset;
97 ipIntValue = ipIntValue >> offset;
98 cidrPrefix = cidrPrefix << offset;
99 ipIntValue = ipIntValue << offset;
100
101 return (cidrPrefix == ipIntValue);
102 }
103
104 private class InternalHostListener implements HostListener {
105
106 /**
Andrea Campanella4b931a92018-06-22 17:28:41 +0200107 * Generate new ACL flow rules for new or updated host following the given ACL rule.
Pengfei Lue0c02e22015-07-07 15:41:31 +0800108 */
109 private void processHostAddedEvent(HostEvent event, AclRule rule) {
110 DeviceId deviceId = event.subject().location().deviceId();
111 for (IpAddress address : event.subject().ipAddresses()) {
112 if ((rule.srcIp() != null) ?
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800113 (checkIpInCidr(address.getIp4Address(), rule.srcIp())) :
114 (checkIpInCidr(address.getIp4Address(), rule.dstIp()))) {
Pengfei Lue0c02e22015-07-07 15:41:31 +0800115 if (!aclStore.checkIfRuleWorksInDevice(rule.id(), deviceId)) {
116 List<RuleId> allowingRuleList = aclStore
117 .getAllowingRuleByDenyingRule(rule.id());
118 if (allowingRuleList != null) {
119 for (RuleId allowingRuleId : allowingRuleList) {
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800120 generateAclFlow(aclStore.getAclRule(allowingRuleId), deviceId);
Pengfei Lue0c02e22015-07-07 15:41:31 +0800121 }
122 }
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800123 generateAclFlow(rule, deviceId);
Pengfei Lue0c02e22015-07-07 15:41:31 +0800124 }
125 }
126 }
127 }
128
129 @Override
130 public void event(HostEvent event) {
Andrea Campanella4b931a92018-06-22 17:28:41 +0200131 // if a new host appears or is updated and an existing rule denies
Pengfei Lue0c02e22015-07-07 15:41:31 +0800132 // its traffic, a new ACL flow rule is generated.
Andrea Campanella4b931a92018-06-22 17:28:41 +0200133 if (event.type() == HostEvent.Type.HOST_ADDED || event.type() == HostEvent.Type.HOST_UPDATED) {
Pengfei Lue0c02e22015-07-07 15:41:31 +0800134 DeviceId deviceId = event.subject().location().deviceId();
135 if (mastershipService.getLocalRole(deviceId) == MastershipRole.MASTER) {
136 for (AclRule rule : aclStore.getAclRules()) {
137 if (rule.action() != AclRule.Action.ALLOW) {
138 processHostAddedEvent(event, rule);
139 }
140 }
141 }
142 }
143 }
144 }
145
146 @Activate
147 public void activate() {
Thomas Vachuska1b1537c2015-12-17 09:06:52 -0800148 appId = coreService.registerApplication("org.onosproject.acl");
Pengfei Lue0c02e22015-07-07 15:41:31 +0800149 hostService.addListener(hostListener);
150 idGenerator = coreService.getIdGenerator("acl-ids");
151 AclRule.bindIdGenerator(idGenerator);
152 log.info("Started");
153 }
154
155 @Deactivate
156 public void deactivate() {
157 hostService.removeListener(hostListener);
sangyun-hanfd286c92017-01-26 16:24:44 +0900158 this.clearAcl();
Pengfei Lue0c02e22015-07-07 15:41:31 +0800159 log.info("Stopped");
160 }
161
162 @Override
163 public List<AclRule> getAclRules() {
164 return aclStore.getAclRules();
165 }
166
167 /**
168 * Checks if the new ACL rule matches an existing rule.
169 * If existing allowing rules matches the new denying rule, store the mappings.
Thomas Vachuska9bb32352015-09-25 11:31:22 -0700170 *
Pengfei Lue0c02e22015-07-07 15:41:31 +0800171 * @return true if the new ACL rule matches an existing rule, false otherwise
172 */
173 private boolean matchCheck(AclRule newRule) {
174 for (AclRule existingRule : aclStore.getAclRules()) {
175 if (newRule.checkMatch(existingRule)) {
176 return true;
177 }
178
179 if (existingRule.action() == AclRule.Action.ALLOW
180 && newRule.action() == AclRule.Action.DENY) {
181 if (existingRule.checkMatch(newRule)) {
182 aclStore.addDenyToAllowMapping(newRule.id(), existingRule.id());
183 }
184 }
185 }
186 return false;
187 }
188
189 @Override
190 public boolean addAclRule(AclRule rule) {
191 if (matchCheck(rule)) {
192 return false;
193 }
194 aclStore.addAclRule(rule);
195 log.info("ACL rule(id:{}) is added.", rule.id());
196 if (rule.action() != AclRule.Action.ALLOW) {
197 enforceRuleAdding(rule);
198 }
199 return true;
200 }
201
202 /**
203 * Gets a set containing all devices connecting with the hosts
204 * whose IP address is in the given CIDR IP address.
205 */
206 private Set<DeviceId> getDeviceIdSet(Ip4Prefix cidrAddr) {
207 Set<DeviceId> deviceIdSet = new HashSet<>();
208 final Iterable<Host> hosts = hostService.getHosts();
209
210 if (cidrAddr.prefixLength() != 32) {
211 for (Host h : hosts) {
212 for (IpAddress a : h.ipAddresses()) {
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800213 if (checkIpInCidr(a.getIp4Address(), cidrAddr)) {
Pengfei Lue0c02e22015-07-07 15:41:31 +0800214 deviceIdSet.add(h.location().deviceId());
215 }
216 }
217 }
218 } else {
219 for (Host h : hosts) {
220 for (IpAddress a : h.ipAddresses()) {
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800221 if (checkIpInCidr(a.getIp4Address(), cidrAddr)) {
Pengfei Lue0c02e22015-07-07 15:41:31 +0800222 deviceIdSet.add(h.location().deviceId());
223 return deviceIdSet;
224 }
225 }
226 }
227 }
228 return deviceIdSet;
229 }
230
231 /**
232 * Enforces denying ACL rule by ACL flow rules.
233 */
234 private void enforceRuleAdding(AclRule rule) {
235 Set<DeviceId> dpidSet;
236 if (rule.srcIp() != null) {
237 dpidSet = getDeviceIdSet(rule.srcIp());
238 } else {
239 dpidSet = getDeviceIdSet(rule.dstIp());
240 }
241
242 for (DeviceId deviceId : dpidSet) {
243 List<RuleId> allowingRuleList = aclStore.getAllowingRuleByDenyingRule(rule.id());
244 if (allowingRuleList != null) {
245 for (RuleId allowingRuleId : allowingRuleList) {
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800246 generateAclFlow(aclStore.getAclRule(allowingRuleId), deviceId);
Pengfei Lue0c02e22015-07-07 15:41:31 +0800247 }
248 }
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800249 generateAclFlow(rule, deviceId);
Pengfei Lue0c02e22015-07-07 15:41:31 +0800250 }
251 }
252
253 /**
254 * Generates ACL flow rule according to ACL rule
255 * and install it into related device.
256 */
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800257 private void generateAclFlow(AclRule rule, DeviceId deviceId) {
Pengfei Lue0c02e22015-07-07 15:41:31 +0800258 if (rule == null || aclStore.checkIfRuleWorksInDevice(rule.id(), deviceId)) {
259 return;
260 }
261
262 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
263 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
264 FlowEntry.Builder flowEntry = DefaultFlowEntry.builder();
265
Nitin Anandfb22901d2018-05-09 12:16:36 +0530266 if (rule.srcMac() != null) {
267 selectorBuilder.matchEthSrc(rule.srcMac());
268
269 }
270 if (rule.dstMac() != null) {
271 selectorBuilder.matchEthDst(rule.dstMac());
272 }
273
Pengfei Lue0c02e22015-07-07 15:41:31 +0800274 selectorBuilder.matchEthType(Ethernet.TYPE_IPV4);
275 if (rule.srcIp() != null) {
276 selectorBuilder.matchIPSrc(rule.srcIp());
277 if (rule.dstIp() != null) {
278 selectorBuilder.matchIPDst(rule.dstIp());
279 }
280 } else {
281 selectorBuilder.matchIPDst(rule.dstIp());
282 }
283 if (rule.ipProto() != 0) {
284 selectorBuilder.matchIPProtocol(Integer.valueOf(rule.ipProto()).byteValue());
285 }
Nitin Anandfb22901d2018-05-09 12:16:36 +0530286 if (rule.dscp() != 0) {
287 selectorBuilder.matchIPDscp(Byte.valueOf(rule.dscp()));
288 }
Pengfei Lue0c02e22015-07-07 15:41:31 +0800289 if (rule.dstTpPort() != 0) {
290 switch (rule.ipProto()) {
291 case IPv4.PROTOCOL_TCP:
Hyunsun Mooncf732fb2015-08-22 21:04:23 -0700292 selectorBuilder.matchTcpDst(TpPort.tpPort(rule.dstTpPort()));
Pengfei Lue0c02e22015-07-07 15:41:31 +0800293 break;
294 case IPv4.PROTOCOL_UDP:
Hyunsun Mooncf732fb2015-08-22 21:04:23 -0700295 selectorBuilder.matchUdpDst(TpPort.tpPort(rule.dstTpPort()));
Pengfei Lue0c02e22015-07-07 15:41:31 +0800296 break;
297 default:
298 break;
299 }
300 }
Nitin Anandfb22901d2018-05-09 12:16:36 +0530301
302 if (rule.srcTpPort() != 0) {
303 switch (rule.ipProto()) {
304 case IPv4.PROTOCOL_TCP:
305 selectorBuilder.matchTcpSrc(TpPort.tpPort(rule.srcTpPort()));
306 break;
307 case IPv4.PROTOCOL_UDP:
308 selectorBuilder.matchUdpSrc(TpPort.tpPort(rule.srcTpPort()));
309 break;
310 default:
311 break;
312 }
313 }
314
Pengfei Lue0c02e22015-07-07 15:41:31 +0800315 if (rule.action() == AclRule.Action.ALLOW) {
316 treatment.add(Instructions.createOutput(PortNumber.CONTROLLER));
317 }
318 flowEntry.forDevice(deviceId);
319 flowEntry.withPriority(aclStore.getPriorityByDevice(deviceId));
320 flowEntry.withSelector(selectorBuilder.build());
321 flowEntry.withTreatment(treatment.build());
322 flowEntry.fromApp(appId);
323 flowEntry.makePermanent();
324 // install flow rule
325 flowRuleService.applyFlowRules(flowEntry.build());
326 log.debug("ACL flow rule {} is installed in {}.", flowEntry.build(), deviceId);
327 aclStore.addRuleToFlowMapping(rule.id(), flowEntry.build());
328 aclStore.addRuleToDeviceMapping(rule.id(), deviceId);
329 }
330
331 @Override
332 public void removeAclRule(RuleId ruleId) {
333 aclStore.removeAclRule(ruleId);
334 log.info("ACL rule(id:{}) is removed.", ruleId);
335 enforceRuleRemoving(ruleId);
336 }
337
338 /**
339 * Enforces removing an existing ACL rule.
340 */
341 private void enforceRuleRemoving(RuleId ruleId) {
342 Set<FlowRule> flowSet = aclStore.getFlowByRule(ruleId);
343 if (flowSet != null) {
344 for (FlowRule flowRule : flowSet) {
345 flowRuleService.removeFlowRules(flowRule);
346 log.debug("ACL flow rule {} is removed from {}.", flowRule.toString(), flowRule.deviceId().toString());
347 }
348 }
349 aclStore.removeRuleToFlowMapping(ruleId);
350 aclStore.removeRuleToDeviceMapping(ruleId);
351 aclStore.removeDenyToAllowMapping(ruleId);
352 }
353
354 @Override
355 public void clearAcl() {
356 aclStore.clearAcl();
357 flowRuleService.removeFlowRulesById(appId);
358 log.info("ACL is cleared.");
359 }
360
361}