blob: dbab8cae4a4f9f390c84fd7614b5415cad7de440 [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.impl;
21
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;
Pengfei Lue0c02e22015-07-07 15:41:31 +080028import org.onos.acl.AclRule;
29import org.onos.acl.AclService;
30import org.onos.acl.AclStore;
31import 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;
Pengfei Lue0c02e22015-07-07 15:41:31 +080037import org.onos.acl.RuleId;
38import 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 */
93 private boolean checkIpInCIDR(Ip4Address ip, Ip4Prefix cidr) {
94 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) ?
114 (checkIpInCIDR(address.getIp4Address(), rule.srcIp())) :
115 (checkIpInCIDR(address.getIp4Address(), rule.dstIp()))) {
116 if (!aclStore.checkIfRuleWorksInDevice(rule.id(), deviceId)) {
117 List<RuleId> allowingRuleList = aclStore
118 .getAllowingRuleByDenyingRule(rule.id());
119 if (allowingRuleList != null) {
120 for (RuleId allowingRuleId : allowingRuleList) {
121 generateACLFlow(aclStore.getAclRule(allowingRuleId), deviceId);
122 }
123 }
124 generateACLFlow(rule, deviceId);
125 }
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.
172 * @return true if the new ACL rule matches an existing rule, false otherwise
173 */
174 private boolean matchCheck(AclRule newRule) {
175 for (AclRule existingRule : aclStore.getAclRules()) {
176 if (newRule.checkMatch(existingRule)) {
177 return true;
178 }
179
180 if (existingRule.action() == AclRule.Action.ALLOW
181 && newRule.action() == AclRule.Action.DENY) {
182 if (existingRule.checkMatch(newRule)) {
183 aclStore.addDenyToAllowMapping(newRule.id(), existingRule.id());
184 }
185 }
186 }
187 return false;
188 }
189
190 @Override
191 public boolean addAclRule(AclRule rule) {
192 if (matchCheck(rule)) {
193 return false;
194 }
195 aclStore.addAclRule(rule);
196 log.info("ACL rule(id:{}) is added.", rule.id());
197 if (rule.action() != AclRule.Action.ALLOW) {
198 enforceRuleAdding(rule);
199 }
200 return true;
201 }
202
203 /**
204 * Gets a set containing all devices connecting with the hosts
205 * whose IP address is in the given CIDR IP address.
206 */
207 private Set<DeviceId> getDeviceIdSet(Ip4Prefix cidrAddr) {
208 Set<DeviceId> deviceIdSet = new HashSet<>();
209 final Iterable<Host> hosts = hostService.getHosts();
210
211 if (cidrAddr.prefixLength() != 32) {
212 for (Host h : hosts) {
213 for (IpAddress a : h.ipAddresses()) {
214 if (checkIpInCIDR(a.getIp4Address(), cidrAddr)) {
215 deviceIdSet.add(h.location().deviceId());
216 }
217 }
218 }
219 } else {
220 for (Host h : hosts) {
221 for (IpAddress a : h.ipAddresses()) {
222 if (checkIpInCIDR(a.getIp4Address(), cidrAddr)) {
223 deviceIdSet.add(h.location().deviceId());
224 return deviceIdSet;
225 }
226 }
227 }
228 }
229 return deviceIdSet;
230 }
231
232 /**
233 * Enforces denying ACL rule by ACL flow rules.
234 */
235 private void enforceRuleAdding(AclRule rule) {
236 Set<DeviceId> dpidSet;
237 if (rule.srcIp() != null) {
238 dpidSet = getDeviceIdSet(rule.srcIp());
239 } else {
240 dpidSet = getDeviceIdSet(rule.dstIp());
241 }
242
243 for (DeviceId deviceId : dpidSet) {
244 List<RuleId> allowingRuleList = aclStore.getAllowingRuleByDenyingRule(rule.id());
245 if (allowingRuleList != null) {
246 for (RuleId allowingRuleId : allowingRuleList) {
247 generateACLFlow(aclStore.getAclRule(allowingRuleId), deviceId);
248 }
249 }
250 generateACLFlow(rule, deviceId);
251 }
252 }
253
254 /**
255 * Generates ACL flow rule according to ACL rule
256 * and install it into related device.
257 */
258 private void generateACLFlow(AclRule rule, DeviceId deviceId) {
259 if (rule == null || aclStore.checkIfRuleWorksInDevice(rule.id(), deviceId)) {
260 return;
261 }
262
263 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
264 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
265 FlowEntry.Builder flowEntry = DefaultFlowEntry.builder();
266
267 selectorBuilder.matchEthType(Ethernet.TYPE_IPV4);
268 if (rule.srcIp() != null) {
269 selectorBuilder.matchIPSrc(rule.srcIp());
270 if (rule.dstIp() != null) {
271 selectorBuilder.matchIPDst(rule.dstIp());
272 }
273 } else {
274 selectorBuilder.matchIPDst(rule.dstIp());
275 }
276 if (rule.ipProto() != 0) {
277 selectorBuilder.matchIPProtocol(Integer.valueOf(rule.ipProto()).byteValue());
278 }
279 if (rule.dstTpPort() != 0) {
280 switch (rule.ipProto()) {
281 case IPv4.PROTOCOL_TCP:
Hyunsun Mooncf732fb2015-08-22 21:04:23 -0700282 selectorBuilder.matchTcpDst(TpPort.tpPort(rule.dstTpPort()));
Pengfei Lue0c02e22015-07-07 15:41:31 +0800283 break;
284 case IPv4.PROTOCOL_UDP:
Hyunsun Mooncf732fb2015-08-22 21:04:23 -0700285 selectorBuilder.matchUdpDst(TpPort.tpPort(rule.dstTpPort()));
Pengfei Lue0c02e22015-07-07 15:41:31 +0800286 break;
287 default:
288 break;
289 }
290 }
291 if (rule.action() == AclRule.Action.ALLOW) {
292 treatment.add(Instructions.createOutput(PortNumber.CONTROLLER));
293 }
294 flowEntry.forDevice(deviceId);
295 flowEntry.withPriority(aclStore.getPriorityByDevice(deviceId));
296 flowEntry.withSelector(selectorBuilder.build());
297 flowEntry.withTreatment(treatment.build());
298 flowEntry.fromApp(appId);
299 flowEntry.makePermanent();
300 // install flow rule
301 flowRuleService.applyFlowRules(flowEntry.build());
302 log.debug("ACL flow rule {} is installed in {}.", flowEntry.build(), deviceId);
303 aclStore.addRuleToFlowMapping(rule.id(), flowEntry.build());
304 aclStore.addRuleToDeviceMapping(rule.id(), deviceId);
305 }
306
307 @Override
308 public void removeAclRule(RuleId ruleId) {
309 aclStore.removeAclRule(ruleId);
310 log.info("ACL rule(id:{}) is removed.", ruleId);
311 enforceRuleRemoving(ruleId);
312 }
313
314 /**
315 * Enforces removing an existing ACL rule.
316 */
317 private void enforceRuleRemoving(RuleId ruleId) {
318 Set<FlowRule> flowSet = aclStore.getFlowByRule(ruleId);
319 if (flowSet != null) {
320 for (FlowRule flowRule : flowSet) {
321 flowRuleService.removeFlowRules(flowRule);
322 log.debug("ACL flow rule {} is removed from {}.", flowRule.toString(), flowRule.deviceId().toString());
323 }
324 }
325 aclStore.removeRuleToFlowMapping(ruleId);
326 aclStore.removeRuleToDeviceMapping(ruleId);
327 aclStore.removeDenyToAllowMapping(ruleId);
328 }
329
330 @Override
331 public void clearAcl() {
332 aclStore.clearAcl();
333 flowRuleService.removeFlowRulesById(appId);
334 log.info("ACL is cleared.");
335 }
336
337}