2 * Copyright (c) 2016 Red Hat, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.netvirt.aclservice;
10 import com.google.common.util.concurrent.ListenableFuture;
11 import java.math.BigInteger;
12 import java.util.ArrayList;
13 import java.util.HashSet;
14 import java.util.List;
16 import java.util.Map.Entry;
19 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
20 import org.opendaylight.genius.datastoreutils.DataStoreJobCoordinator;
21 import org.opendaylight.genius.mdsalutil.ActionInfo;
22 import org.opendaylight.genius.mdsalutil.FlowEntity;
23 import org.opendaylight.genius.mdsalutil.InstructionInfo;
24 import org.opendaylight.genius.mdsalutil.MDSALUtil;
25 import org.opendaylight.genius.mdsalutil.MatchInfoBase;
26 import org.opendaylight.genius.mdsalutil.NwConstants;
27 import org.opendaylight.genius.mdsalutil.actions.ActionNxResubmit;
28 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
29 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
30 import org.opendaylight.netvirt.aclservice.api.AclServiceListener;
31 import org.opendaylight.netvirt.aclservice.api.AclServiceManager.Action;
32 import org.opendaylight.netvirt.aclservice.api.utils.AclInterface;
33 import org.opendaylight.netvirt.aclservice.api.utils.AclInterfaceCacheUtil;
34 import org.opendaylight.netvirt.aclservice.utils.AclDataUtil;
35 import org.opendaylight.netvirt.aclservice.utils.AclServiceUtils;
36 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.Acl;
37 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.AccessListEntries;
38 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.Ace;
39 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceModeBase;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceModeEgress;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.SecurityRuleAttr;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.interfaces._interface.AllowedAddressPairs;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
47 public abstract class AbstractAclServiceImpl implements AclServiceListener {
49 private static final Logger LOG = LoggerFactory.getLogger(AbstractAclServiceImpl.class);
51 protected final IMdsalApiManager mdsalManager;
52 protected final DataBroker dataBroker;
53 protected final Class<? extends ServiceModeBase> serviceMode;
54 protected final AclDataUtil aclDataUtil;
55 protected final AclServiceUtils aclServiceUtils;
58 * Initialize the member variables.
63 * the data broker instance.
65 * the mdsal manager instance.
68 * @param aclServiceUtils
69 * the acl service util.
71 public AbstractAclServiceImpl(Class<? extends ServiceModeBase> serviceMode, DataBroker dataBroker,
72 IMdsalApiManager mdsalManager, AclDataUtil aclDataUtil, AclServiceUtils aclServiceUtils) {
73 this.dataBroker = dataBroker;
74 this.mdsalManager = mdsalManager;
75 this.serviceMode = serviceMode;
76 this.aclDataUtil = aclDataUtil;
77 this.aclServiceUtils = aclServiceUtils;
81 public boolean applyAcl(AclInterface port) {
83 LOG.error("port cannot be null");
86 if (port.getSecurityGroups() == null) {
87 LOG.error("port security groups cannot be null");
90 BigInteger dpId = port.getDpId();
91 if (dpId == null || port.getLPortTag() == null) {
92 LOG.error("Unable to find DpId from ACL interface with id {}", port.getInterfaceId());
95 LOG.debug("Applying ACL on port {} with DpId {}", port, dpId);
96 programAclWithAllowedAddress(port, port.getAllowedAddressPairs(), Action.ADD, NwConstants.ADD_FLOW);
97 updateRemoteAclFilterTable(port, NwConstants.ADD_FLOW);
102 public boolean bindAcl(AclInterface port) {
103 if (port == null || port.getSecurityGroups() == null) {
104 LOG.error("port and port security groups cannot be null");
108 if (port.getDpId() != null) {
109 updateRemoteAclFilterTable(port, NwConstants.ADD_FLOW);
115 public boolean unbindAcl(AclInterface port) {
116 BigInteger dpId = port.getDpId();
118 LOG.error("Unable to find DpId from ACL interface with id {}", port.getInterfaceId());
122 updateRemoteAclFilterTable(port, NwConstants.DEL_FLOW);
127 public boolean updateAcl(AclInterface portBefore, AclInterface portAfter) {
128 boolean result = true;
129 boolean isPortSecurityEnable = portAfter.getPortSecurityEnabled();
130 boolean isPortSecurityEnableBefore = portBefore.getPortSecurityEnabled();
131 // if port security is changed, apply/remove Acls
132 if (isPortSecurityEnableBefore != isPortSecurityEnable) {
133 LOG.debug("On ACL update, Port security is {} for {}", isPortSecurityEnable ? "Enabled" :
134 "Disabled", portAfter.getInterfaceId());
135 if (isPortSecurityEnable) {
136 result = applyAcl(portAfter) && bindAcl(portAfter);
138 result = removeAcl(portBefore) && unbindAcl(portBefore);
140 } else if (isPortSecurityEnable) {
141 // Acls has been updated, find added/removed Acls and act accordingly.
142 processInterfaceUpdate(portBefore, portAfter);
143 LOG.debug("On ACL update, ACL has been updated for {}", portAfter.getInterfaceId());
149 private void processInterfaceUpdate(AclInterface portBefore, AclInterface portAfter) {
150 BigInteger dpId = portAfter.getDpId();
151 List<AllowedAddressPairs> addedAllowedAddressPairs =
152 AclServiceUtils.getUpdatedAllowedAddressPairs(portAfter.getAllowedAddressPairs(),
153 portBefore.getAllowedAddressPairs());
154 List<AllowedAddressPairs> deletedAllowedAddressPairs =
155 AclServiceUtils.getUpdatedAllowedAddressPairs(portBefore.getAllowedAddressPairs(),
156 portAfter.getAllowedAddressPairs());
157 if (deletedAllowedAddressPairs != null && !deletedAllowedAddressPairs.isEmpty()) {
158 programAclWithAllowedAddress(portAfter, deletedAllowedAddressPairs, Action.UPDATE, NwConstants.DEL_FLOW);
160 if (addedAllowedAddressPairs != null && !addedAllowedAddressPairs.isEmpty()) {
161 programAclWithAllowedAddress(portAfter, addedAllowedAddressPairs, Action.UPDATE, NwConstants.ADD_FLOW);
163 updateArpForAllowedAddressPairs(dpId, portAfter.getLPortTag(), deletedAllowedAddressPairs,
164 portAfter.getAllowedAddressPairs());
166 updateAclInterfaceInCache(portBefore);
167 // Have to delete and add all rules because there can be following scenario: Interface1 with SG1, Interface2
168 // with SG2 (which has ACE with remote SG1). Now When we add SG3 to Interface1, the rule for Interface2 which
169 // match the IP of Interface1 will not be installed (but it have to be because Interface1 has more than one SG).
170 // So we need to remove all rules and install them from 0, and we cannot handle only the delta.
171 updateCustomRules(portBefore, portBefore.getSecurityGroups(), NwConstants.DEL_FLOW,
172 portAfter.getAllowedAddressPairs());
173 updateRemoteAclFilterTable(portBefore, NwConstants.DEL_FLOW);
175 updateAclInterfaceInCache(portAfter);
177 updateCustomRules(portAfter, portAfter.getSecurityGroups(), NwConstants.ADD_FLOW,
178 portAfter.getAllowedAddressPairs());
179 updateRemoteAclFilterTable(portAfter, NwConstants.ADD_FLOW);
182 private void updateAclInterfaceInCache(AclInterface aclInterfaceNew) {
183 AclInterfaceCacheUtil.addAclInterfaceToCache(aclInterfaceNew.getInterfaceId(), aclInterfaceNew);
184 aclDataUtil.addOrUpdateAclInterfaceMap(aclInterfaceNew.getSecurityGroups(), aclInterfaceNew);
187 private void updateCustomRules(AclInterface port, List<Uuid> aclUuidList, int action,
188 List<AllowedAddressPairs> syncAllowedAddresses) {
189 programAclRules(port, aclUuidList, action);
190 syncRemoteAclRules(aclUuidList, action, port.getInterfaceId(), syncAllowedAddresses);
193 private void syncRemoteAclRules(List<Uuid> aclUuidList, int action, String currentPortId,
194 List<AllowedAddressPairs> syncAllowedAddresses) {
195 if (aclUuidList == null) {
196 LOG.warn("security groups are null");
200 for (Uuid remoteAclId : aclUuidList) {
201 Map<String, Set<AclInterface>> mapAclWithPortSet = aclDataUtil.getRemoteAclInterfaces(remoteAclId);
202 if (mapAclWithPortSet == null) {
205 for (Entry<String, Set<AclInterface>> entry : mapAclWithPortSet.entrySet()) {
206 String aclName = entry.getKey();
207 for (AclInterface port : entry.getValue()) {
208 if (currentPortId.equals(port.getInterfaceId())
209 || (port.getSecurityGroups() != null && port.getSecurityGroups().size() == 1)) {
212 List<Ace> remoteAceList = AclServiceUtils.getAceWithRemoteAclId(dataBroker, port, remoteAclId);
213 for (Ace ace : remoteAceList) {
214 programAceRule(port, action, aclName, ace, syncAllowedAddresses);
221 private void programAclWithAllowedAddress(AclInterface port, List<AllowedAddressPairs> allowedAddresses,
222 Action action, int addOrRemove) {
223 BigInteger dpId = port.getDpId();
224 int lportTag = port.getLPortTag();
225 LOG.debug("Applying ACL Allowed Address on DpId {}, lportTag {}, Action {}", dpId, lportTag, action);
226 List<Uuid> aclUuidList = port.getSecurityGroups();
227 String portId = port.getInterfaceId();
228 programGeneralFixedRules(dpId, "", allowedAddresses, lportTag, action, addOrRemove);
229 programSpecificFixedRules(dpId, "", allowedAddresses, lportTag, portId, action, addOrRemove);
230 if (action == Action.ADD || action == Action.REMOVE) {
231 programAclRules(port, aclUuidList, addOrRemove);
233 syncRemoteAclRules(aclUuidList, addOrRemove, portId, allowedAddresses);
238 public boolean removeAcl(AclInterface port) {
239 BigInteger dpId = port.getDpId();
241 LOG.error("Unable to find DP Id from ACL interface with id {}", port.getInterfaceId());
244 programAclWithAllowedAddress(port, port.getAllowedAddressPairs(), Action.REMOVE, NwConstants.DEL_FLOW);
245 updateRemoteAclFilterTable(port, NwConstants.DEL_FLOW, true);
250 public boolean applyAce(AclInterface port, String aclName, Ace ace) {
251 if (!port.isPortSecurityEnabled()) {
254 programAceRule(port, NwConstants.ADD_FLOW, aclName, ace, null);
255 updateRemoteAclFilterTable(port, NwConstants.ADD_FLOW);
260 public boolean removeAce(AclInterface port, String aclName, Ace ace) {
261 if (!port.isPortSecurityEnabled()) {
264 programAceRule(port, NwConstants.DEL_FLOW, aclName, ace, null);
265 updateRemoteAclFilterTable(port, NwConstants.DEL_FLOW);
272 * @param aclInterface the acl interface
274 public abstract void bindService(AclInterface aclInterface);
279 * @param aclInterface the acl interface
281 protected abstract void unbindService(AclInterface aclInterface);
284 * Program the default anti-spoofing rules.
286 * @param dpid the dpid
287 * @param dhcpMacAddress the dhcp mac address.
288 * @param allowedAddresses the allowed addresses
289 * @param lportTag the lport tag
290 * @param action add/modify/remove action
291 * @param addOrRemove addorRemove
293 protected abstract void programGeneralFixedRules(BigInteger dpid, String dhcpMacAddress,
294 List<AllowedAddressPairs> allowedAddresses, int lportTag, Action action, int addOrRemove);
297 * Update arp for allowed address pairs.
299 * @param dpId the dp id
300 * @param lportTag the lport tag
301 * @param deletedAAP the deleted allowed address pairs
302 * @param addedAAP the added allowed address pairs
304 protected abstract void updateArpForAllowedAddressPairs(BigInteger dpId, int lportTag,
305 List<AllowedAddressPairs> deletedAAP, List<AllowedAddressPairs> addedAAP);
308 * Program the default specific rules.
310 * @param dpid the dpid
311 * @param dhcpMacAddress the dhcp mac address.
312 * @param allowedAddresses the allowed addresses
313 * @param lportTag the lport tag
314 * @param portId the port id
315 * @param action add/modify/remove action
316 * @param addOrRemove addorRemove
318 protected abstract void programSpecificFixedRules(BigInteger dpid, String dhcpMacAddress,
319 List<AllowedAddressPairs> allowedAddresses, int lportTag, String portId, Action action, int addOrRemove);
322 * Programs the acl custom rules.
324 * @param port acl interface
325 * @param aclUuidList the list of acl uuid to be applied
326 * @param addOrRemove whether to delete or add flow
327 * @return program succeeded
329 protected abstract boolean programAclRules(AclInterface port, List<Uuid> aclUuidList, int addOrRemove);
332 * Programs the ace custom rule.
334 * @param port acl interface
335 * @param addOrRemove whether to delete or add flow
336 * @param aclName the acl name
337 * @param ace rule to be program
338 * @param syncAllowedAddresses the allowed addresses
340 protected abstract void programAceRule(AclInterface port, int addOrRemove, String aclName, Ace ace,
341 List<AllowedAddressPairs> syncAllowedAddresses);
344 * Writes/remove the flow to/from the datastore.
363 * the list of matches to be writted
364 * @param instructions
365 * the list of instruction to be written.
367 * add or remove the entries.
369 protected void syncFlow(BigInteger dpId, short tableId, String flowId, int priority, String flowName,
370 int idleTimeOut, int hardTimeOut, BigInteger cookie, List<? extends MatchInfoBase> matches,
371 List<InstructionInfo> instructions, int addOrRemove) {
372 DataStoreJobCoordinator dataStoreCoordinator = DataStoreJobCoordinator.getInstance();
373 dataStoreCoordinator.enqueueJob(flowName, () -> {
374 List<ListenableFuture<Void>> futures = new ArrayList<>();
375 if (addOrRemove == NwConstants.DEL_FLOW) {
376 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, tableId, flowId, priority, flowName,
377 idleTimeOut, hardTimeOut, cookie, matches, null);
378 LOG.trace("Removing Acl Flow DpnId {}, flowId {}", dpId, flowId);
380 futures.add(mdsalManager.removeFlow(dpId, flowEntity));
383 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, tableId, flowId, priority, flowName,
384 idleTimeOut, hardTimeOut, cookie, matches, instructions);
385 LOG.trace("Installing DpnId {}, flowId {}", dpId, flowId);
386 futures.add(mdsalManager.installFlow(dpId, flowEntity));
393 * Gets the dispatcher table resubmit instructions based on ingress/egress service mode w.r.t switch.
395 * @param actionsInfos
397 * @return the instructions for dispatcher table resubmit
399 protected List<InstructionInfo> getDispatcherTableResubmitInstructions(List<ActionInfo> actionsInfos) {
400 short dispatcherTableId = NwConstants.LPORT_DISPATCHER_TABLE;
401 if (ServiceModeEgress.class.equals(this.serviceMode)) {
402 dispatcherTableId = NwConstants.EGRESS_LPORT_DISPATCHER_TABLE;
405 List<InstructionInfo> instructions = new ArrayList<>();
406 actionsInfos.add(new ActionNxResubmit(dispatcherTableId));
407 instructions.add(new InstructionApplyActions(actionsInfos));
411 private void updateRemoteAclFilterTable(AclInterface port, int addOrRemove) {
412 updateRemoteAclFilterTable(port, addOrRemove, false);
415 private void updateRemoteAclFilterTable(AclInterface port, int addOrRemove, boolean isAclDeleted) {
416 if (port.getSecurityGroups() == null) {
417 LOG.debug("Port {} without SGs", port.getInterfaceId());
421 if (AclServiceUtils.exactlyOneAcl(port)) {
422 Uuid acl = port.getSecurityGroups().get(0);
423 BigInteger aclId = aclServiceUtils.buildAclId(acl);
424 if (aclDataUtil.getRemoteAcl(acl) != null) {
425 Map<String, Set<AclInterface>> mapAclWithPortSet = aclDataUtil.getRemoteAclInterfaces(acl);
426 Set<BigInteger> dpns = collectDpns(mapAclWithPortSet);
428 for (AllowedAddressPairs ip : port.getAllowedAddressPairs()) {
429 if (!AclServiceUtils.isNotIpv4AllNetwork(ip)) {
432 for (BigInteger dpId : dpns) {
433 updateRemoteAclTableForPort(port, acl, addOrRemove, ip, aclId, dpId);
436 syncRemoteAclTableFromOtherDpns(port, acl, aclId, addOrRemove);
438 LOG.debug("Port {} with more than one SG ({}). Don't change ACL filter table", port.getInterfaceId(),
439 port.getSecurityGroups().size());
441 } else if (port.getSecurityGroups() != null && port.getSecurityGroups().size() > 1) {
442 updateRemoteAclTableForMultipleAcls(port, addOrRemove, port.getInterfaceId());
444 syncRemoteAclTable(port, addOrRemove, port.getInterfaceId(), isAclDeleted);
447 private void syncRemoteAclTableFromOtherDpns(AclInterface port, Uuid acl, BigInteger aclId, int addOrRemove) {
448 List<AclInterface> aclInterfaces = aclDataUtil.getInterfaceList(acl);
449 BigInteger dpId = port.getDpId();
450 boolean isFirstPortInDpn = true;
451 if (aclInterfaces != null) {
452 for (AclInterface aclInterface : aclInterfaces) {
453 if (port.getInterfaceId().equals(aclInterface.getInterfaceId())) {
456 if (dpId.equals(aclInterface.getDpId())) {
457 isFirstPortInDpn = false;
461 if (isFirstPortInDpn) {
462 for (AclInterface aclInterface : aclInterfaces) {
463 if (port.getInterfaceId().equals(aclInterface.getInterfaceId())) {
466 for (AllowedAddressPairs ip : aclInterface.getAllowedAddressPairs()) {
467 updateRemoteAclTableForPort(aclInterface, acl, addOrRemove, ip, aclId, port.getDpId());
474 private void syncRemoteAclTable(AclInterface port, int addOrRemove, String ignorePort, boolean isAclDeleted) {
475 for (Uuid aclUuid : port.getSecurityGroups()) {
476 if (aclDataUtil.getRemoteAcl(aclUuid) == null) {
479 List<AclInterface> aclInterfaces = aclDataUtil.getInterfaceList(aclUuid);
480 if (aclInterfaces != null) {
481 for (AclInterface aclInterface : aclInterfaces) {
482 if (aclInterface.getInterfaceId().equals(port.getInterfaceId())
483 || AclServiceUtils.exactlyOneAcl(aclInterface)) {
486 boolean allMultipleAcls = true;
487 List<Uuid> remoteInterfaceRemoteAcls = aclInterface.getSecurityGroups();
488 if (remoteInterfaceRemoteAcls != null) {
489 for (Uuid remoteInterfaceRemoteAcl : remoteInterfaceRemoteAcls) {
490 if (aclDataUtil.getRemoteAcl(remoteInterfaceRemoteAcl) == null) {
493 List<AclInterface> aclInterfaces2 = aclDataUtil.getInterfaceList(remoteInterfaceRemoteAcl);
494 if (aclInterfaces2 != null) {
495 for (AclInterface aclInterface2 : aclInterfaces2) {
496 if (aclInterface2.getInterfaceId().equals(aclInterface.getInterfaceId())) {
499 if (aclInterface2.getSecurityGroups().size() == 1) {
500 allMultipleAcls = false;
507 int addRremove = (allMultipleAcls) ? NwConstants.DEL_FLOW : NwConstants.ADD_FLOW;
508 addRremove = (isAclDeleted) ? NwConstants.DEL_FLOW : addRremove;
509 for (AllowedAddressPairs ip : aclInterface.getAllowedAddressPairs()) {
510 if (!AclServiceUtils.isNotIpv4AllNetwork(ip)) {
513 updateRemoteAclTableForPort(aclInterface, aclUuid, addRremove, ip,
514 aclServiceUtils.buildAclId(aclUuid), aclInterface.getDpId());
521 private void updateRemoteAclTableForMultipleAcls(AclInterface port, int addOrRemove, String ignorePort) {
522 BigInteger dpId = port.getDpId();
524 LOG.warn("trying to write to null dpnId");
527 for (Uuid aclUuid : port.getSecurityGroups()) {
528 if (aclDataUtil.getRemoteAcl(aclUuid) == null) {
531 Acl acl = AclServiceUtils.getAcl(dataBroker, aclUuid.getValue());
533 LOG.debug("The ACL {} is empty", aclUuid);
537 Map<String, Set<AclInterface>> mapAclWithPortSet = aclDataUtil.getRemoteAclInterfaces(aclUuid);
538 Set<BigInteger> dpns = collectDpns(mapAclWithPortSet);
540 AccessListEntries accessListEntries = acl.getAccessListEntries();
541 List<Ace> aceList = accessListEntries.getAce();
542 for (Ace ace : aceList) {
543 SecurityRuleAttr aceAttr = AclServiceUtils.getAccesssListAttributes(ace);
544 if (aceAttr.getRemoteGroupId() == null) {
547 List<AclInterface> interfaceList = aclDataUtil.getInterfaceList(aceAttr.getRemoteGroupId());
548 if (interfaceList == null) {
552 for (AclInterface inter : interfaceList) {
553 if (ignorePort.equals(inter.getInterfaceId())) {
556 if (inter.getSecurityGroups() != null && inter.getSecurityGroups().size() == 1) {
557 BigInteger aclId = aclServiceUtils.buildAclId(aceAttr.getRemoteGroupId());
558 for (AllowedAddressPairs ip : port.getAllowedAddressPairs()) {
559 if (!AclServiceUtils.isNotIpv4AllNetwork(ip)) {
562 for (BigInteger dpnId : dpns) {
563 updateRemoteAclTableForPort(port, aceAttr.getRemoteGroupId(), addOrRemove, ip, aclId,
567 syncRemoteAclTableFromOtherDpns(port, aclUuid, aclId, addOrRemove);
574 protected abstract void updateRemoteAclTableForPort(AclInterface port, Uuid acl, int addOrRemove,
575 AllowedAddressPairs ip, BigInteger aclId, BigInteger dpId);
577 protected String getOperAsString(int flowOper) {
580 case NwConstants.ADD_FLOW:
583 case NwConstants.DEL_FLOW:
586 case NwConstants.MOD_FLOW:
595 protected Set<BigInteger> collectDpns(Map<String, Set<AclInterface>> mapAclWithPortSet) {
596 Set<BigInteger> dpns = new HashSet<>();
597 if (mapAclWithPortSet == null) {
600 for (Set<AclInterface> innerSet : mapAclWithPortSet.values()) {
601 if (innerSet == null) {
604 for (AclInterface inter : innerSet) {
605 dpns.add(inter.getDpId());
611 protected char[] getIpPrefixOrAddress(AllowedAddressPairs ip) {
612 if (ip.getIpAddress().getIpAddress() != null) {
613 return ip.getIpAddress().getIpAddress().getValue();
614 } else if (ip.getIpAddress().getIpPrefix() != null) {
615 return ip.getIpAddress().getIpPrefix().getValue();