blob: 301a477a78045040d12603ba1d6ea32ca84e9924 [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() {
Thomas Vachuska1b1537c2015-12-17 09:06:52 -0800149 appId = coreService.registerApplication("org.onosproject.acl");
Pengfei Lue0c02e22015-07-07 15:41:31 +0800150 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);
Brian O'Connorf1781632015-12-11 15:34:03 -0800159 // TODO can be replaced with this.clearAcl()
Pengfei Lue0c02e22015-07-07 15:41:31 +0800160 flowRuleService.removeFlowRulesById(appId);
161 aclStore.clearAcl();
162 log.info("Stopped");
163 }
164
165 @Override
166 public List<AclRule> getAclRules() {
167 return aclStore.getAclRules();
168 }
169
170 /**
171 * Checks if the new ACL rule matches an existing rule.
172 * If existing allowing rules matches the new denying rule, store the mappings.
Thomas Vachuska9bb32352015-09-25 11:31:22 -0700173 *
Pengfei Lue0c02e22015-07-07 15:41:31 +0800174 * @return true if the new ACL rule matches an existing rule, false otherwise
175 */
176 private boolean matchCheck(AclRule newRule) {
177 for (AclRule existingRule : aclStore.getAclRules()) {
178 if (newRule.checkMatch(existingRule)) {
179 return true;
180 }
181
182 if (existingRule.action() == AclRule.Action.ALLOW
183 && newRule.action() == AclRule.Action.DENY) {
184 if (existingRule.checkMatch(newRule)) {
185 aclStore.addDenyToAllowMapping(newRule.id(), existingRule.id());
186 }
187 }
188 }
189 return false;
190 }
191
192 @Override
193 public boolean addAclRule(AclRule rule) {
194 if (matchCheck(rule)) {
195 return false;
196 }
197 aclStore.addAclRule(rule);
198 log.info("ACL rule(id:{}) is added.", rule.id());
199 if (rule.action() != AclRule.Action.ALLOW) {
200 enforceRuleAdding(rule);
201 }
202 return true;
203 }
204
205 /**
206 * Gets a set containing all devices connecting with the hosts
207 * whose IP address is in the given CIDR IP address.
208 */
209 private Set<DeviceId> getDeviceIdSet(Ip4Prefix cidrAddr) {
210 Set<DeviceId> deviceIdSet = new HashSet<>();
211 final Iterable<Host> hosts = hostService.getHosts();
212
213 if (cidrAddr.prefixLength() != 32) {
214 for (Host h : hosts) {
215 for (IpAddress a : h.ipAddresses()) {
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800216 if (checkIpInCidr(a.getIp4Address(), cidrAddr)) {
Pengfei Lue0c02e22015-07-07 15:41:31 +0800217 deviceIdSet.add(h.location().deviceId());
218 }
219 }
220 }
221 } else {
222 for (Host h : hosts) {
223 for (IpAddress a : h.ipAddresses()) {
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800224 if (checkIpInCidr(a.getIp4Address(), cidrAddr)) {
Pengfei Lue0c02e22015-07-07 15:41:31 +0800225 deviceIdSet.add(h.location().deviceId());
226 return deviceIdSet;
227 }
228 }
229 }
230 }
231 return deviceIdSet;
232 }
233
234 /**
235 * Enforces denying ACL rule by ACL flow rules.
236 */
237 private void enforceRuleAdding(AclRule rule) {
238 Set<DeviceId> dpidSet;
239 if (rule.srcIp() != null) {
240 dpidSet = getDeviceIdSet(rule.srcIp());
241 } else {
242 dpidSet = getDeviceIdSet(rule.dstIp());
243 }
244
245 for (DeviceId deviceId : dpidSet) {
246 List<RuleId> allowingRuleList = aclStore.getAllowingRuleByDenyingRule(rule.id());
247 if (allowingRuleList != null) {
248 for (RuleId allowingRuleId : allowingRuleList) {
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800249 generateAclFlow(aclStore.getAclRule(allowingRuleId), deviceId);
Pengfei Lue0c02e22015-07-07 15:41:31 +0800250 }
251 }
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800252 generateAclFlow(rule, deviceId);
Pengfei Lue0c02e22015-07-07 15:41:31 +0800253 }
254 }
255
256 /**
257 * Generates ACL flow rule according to ACL rule
258 * and install it into related device.
259 */
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800260 private void generateAclFlow(AclRule rule, DeviceId deviceId) {
Pengfei Lue0c02e22015-07-07 15:41:31 +0800261 if (rule == null || aclStore.checkIfRuleWorksInDevice(rule.id(), deviceId)) {
262 return;
263 }
264
265 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
266 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
267 FlowEntry.Builder flowEntry = DefaultFlowEntry.builder();
268
269 selectorBuilder.matchEthType(Ethernet.TYPE_IPV4);
270 if (rule.srcIp() != null) {
271 selectorBuilder.matchIPSrc(rule.srcIp());
272 if (rule.dstIp() != null) {
273 selectorBuilder.matchIPDst(rule.dstIp());
274 }
275 } else {
276 selectorBuilder.matchIPDst(rule.dstIp());
277 }
278 if (rule.ipProto() != 0) {
279 selectorBuilder.matchIPProtocol(Integer.valueOf(rule.ipProto()).byteValue());
280 }
281 if (rule.dstTpPort() != 0) {
282 switch (rule.ipProto()) {
283 case IPv4.PROTOCOL_TCP:
Hyunsun Mooncf732fb2015-08-22 21:04:23 -0700284 selectorBuilder.matchTcpDst(TpPort.tpPort(rule.dstTpPort()));
Pengfei Lue0c02e22015-07-07 15:41:31 +0800285 break;
286 case IPv4.PROTOCOL_UDP:
Hyunsun Mooncf732fb2015-08-22 21:04:23 -0700287 selectorBuilder.matchUdpDst(TpPort.tpPort(rule.dstTpPort()));
Pengfei Lue0c02e22015-07-07 15:41:31 +0800288 break;
289 default:
290 break;
291 }
292 }
293 if (rule.action() == AclRule.Action.ALLOW) {
294 treatment.add(Instructions.createOutput(PortNumber.CONTROLLER));
295 }
296 flowEntry.forDevice(deviceId);
297 flowEntry.withPriority(aclStore.getPriorityByDevice(deviceId));
298 flowEntry.withSelector(selectorBuilder.build());
299 flowEntry.withTreatment(treatment.build());
300 flowEntry.fromApp(appId);
301 flowEntry.makePermanent();
302 // install flow rule
303 flowRuleService.applyFlowRules(flowEntry.build());
304 log.debug("ACL flow rule {} is installed in {}.", flowEntry.build(), deviceId);
305 aclStore.addRuleToFlowMapping(rule.id(), flowEntry.build());
306 aclStore.addRuleToDeviceMapping(rule.id(), deviceId);
307 }
308
309 @Override
310 public void removeAclRule(RuleId ruleId) {
311 aclStore.removeAclRule(ruleId);
312 log.info("ACL rule(id:{}) is removed.", ruleId);
313 enforceRuleRemoving(ruleId);
314 }
315
316 /**
317 * Enforces removing an existing ACL rule.
318 */
319 private void enforceRuleRemoving(RuleId ruleId) {
320 Set<FlowRule> flowSet = aclStore.getFlowByRule(ruleId);
321 if (flowSet != null) {
322 for (FlowRule flowRule : flowSet) {
323 flowRuleService.removeFlowRules(flowRule);
324 log.debug("ACL flow rule {} is removed from {}.", flowRule.toString(), flowRule.deviceId().toString());
325 }
326 }
327 aclStore.removeRuleToFlowMapping(ruleId);
328 aclStore.removeRuleToDeviceMapping(ruleId);
329 aclStore.removeDenyToAllowMapping(ruleId);
330 }
331
332 @Override
333 public void clearAcl() {
334 aclStore.clearAcl();
335 flowRuleService.removeFlowRulesById(appId);
336 log.info("ACL is cleared.");
337 }
338
339}