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.collect.Lists;
11 import java.math.BigInteger;
12 import java.util.ArrayList;
13 import java.util.Collection;
14 import java.util.Collections;
15 import java.util.HashMap;
16 import java.util.HashSet;
17 import java.util.List;
19 import java.util.Map.Entry;
21 import java.util.SortedSet;
22 import java.util.stream.Collectors;
23 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
24 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
25 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
26 import org.opendaylight.genius.mdsalutil.ActionInfo;
27 import org.opendaylight.genius.mdsalutil.FlowEntity;
28 import org.opendaylight.genius.mdsalutil.InstructionInfo;
29 import org.opendaylight.genius.mdsalutil.MDSALUtil;
30 import org.opendaylight.genius.mdsalutil.MatchInfoBase;
31 import org.opendaylight.genius.mdsalutil.NwConstants;
32 import org.opendaylight.genius.mdsalutil.actions.ActionNxConntrack;
33 import org.opendaylight.genius.mdsalutil.actions.ActionNxConntrack.NxCtAction;
34 import org.opendaylight.genius.mdsalutil.actions.ActionNxResubmit;
35 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
36 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
37 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
38 import org.opendaylight.genius.mdsalutil.matches.MatchMetadata;
39 import org.opendaylight.genius.mdsalutil.nxmatches.NxMatchCtState;
40 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
41 import org.opendaylight.netvirt.aclservice.api.AclInterfaceCache;
42 import org.opendaylight.netvirt.aclservice.api.AclServiceListener;
43 import org.opendaylight.netvirt.aclservice.api.AclServiceManager.Action;
44 import org.opendaylight.netvirt.aclservice.api.utils.AclInterface;
45 import org.opendaylight.netvirt.aclservice.utils.AclConntrackClassifierType;
46 import org.opendaylight.netvirt.aclservice.utils.AclConstants;
47 import org.opendaylight.netvirt.aclservice.utils.AclDataUtil;
48 import org.opendaylight.netvirt.aclservice.utils.AclServiceOFFlowBuilder;
49 import org.opendaylight.netvirt.aclservice.utils.AclServiceUtils;
50 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.Acl;
51 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.AccessListEntries;
52 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.Ace;
53 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.ace.Matches;
54 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.ace.matches.ace.type.AceIp;
55 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.ace.ip.version.AceIpv4;
56 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceModeBase;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceModeEgress;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.DirectionBase;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.DirectionEgress;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.DirectionIngress;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.SecurityRuleAttr;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.interfaces._interface.AllowedAddressPairs;
64 import org.slf4j.Logger;
65 import org.slf4j.LoggerFactory;
67 public abstract class AbstractAclServiceImpl implements AclServiceListener {
69 private static final Logger LOG = LoggerFactory.getLogger(AbstractAclServiceImpl.class);
71 protected final IMdsalApiManager mdsalManager;
72 protected final DataBroker dataBroker;
73 protected final ManagedNewTransactionRunner txRunner;
74 protected final Class<? extends ServiceModeBase> serviceMode;
75 protected final AclDataUtil aclDataUtil;
76 protected final AclServiceUtils aclServiceUtils;
77 protected final JobCoordinator jobCoordinator;
78 protected final AclInterfaceCache aclInterfaceCache;
80 protected final Class<? extends DirectionBase> direction;
81 protected final String directionString;
84 * Initialize the member variables.
86 * @param serviceMode the service mode
87 * @param dataBroker the data broker instance.
88 * @param mdsalManager the mdsal manager instance.
89 * @param aclDataUtil the acl data util.
90 * @param aclServiceUtils the acl service util.
91 * @param jobCoordinator the job coordinator
92 * @param aclInterfaceCache the acl interface cache
94 public AbstractAclServiceImpl(Class<? extends ServiceModeBase> serviceMode, DataBroker dataBroker,
95 IMdsalApiManager mdsalManager, AclDataUtil aclDataUtil, AclServiceUtils aclServiceUtils,
96 JobCoordinator jobCoordinator, AclInterfaceCache aclInterfaceCache) {
97 this.dataBroker = dataBroker;
98 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
99 this.mdsalManager = mdsalManager;
100 this.serviceMode = serviceMode;
101 this.aclDataUtil = aclDataUtil;
102 this.aclServiceUtils = aclServiceUtils;
103 this.jobCoordinator = jobCoordinator;
104 this.aclInterfaceCache = aclInterfaceCache;
107 this.serviceMode.equals(ServiceModeEgress.class) ? DirectionIngress.class : DirectionEgress.class;
108 this.directionString = this.direction.equals(DirectionEgress.class) ? "Egress" : "Ingress";
112 public boolean applyAcl(AclInterface port) {
114 LOG.error("port cannot be null");
117 if (port.getSecurityGroups() == null) {
118 LOG.info("Port {} without SGs", port.getInterfaceId());
121 BigInteger dpId = port.getDpId();
122 if (dpId == null || port.getLPortTag() == null) {
123 LOG.error("Unable to find DpId from ACL interface with id {}", port.getInterfaceId());
126 LOG.debug("Applying ACL on port {} with DpId {}", port, dpId);
127 programAcl(port, Action.ADD, NwConstants.ADD_FLOW);
128 updateRemoteAclFilterTable(port, NwConstants.ADD_FLOW);
133 public boolean bindAcl(AclInterface port) {
134 if (port == null || port.getSecurityGroups() == null) {
135 LOG.error("Port and port security groups cannot be null for binding ACL service, port={}", port);
139 if (port.getDpId() != null) {
140 updateRemoteAclFilterTable(port, NwConstants.ADD_FLOW);
146 public boolean unbindAcl(AclInterface port) {
148 LOG.error("Port cannot be null for unbinding ACL service");
151 if (port.getDpId() != null) {
153 updateRemoteAclFilterTable(port, NwConstants.DEL_FLOW);
159 public boolean updateAcl(AclInterface portBefore, AclInterface portAfter) {
160 // this check is to avoid situations of port update coming before interface state is up
161 if (portAfter.getDpId() == null || portAfter.getLPortTag() == null) {
162 LOG.debug("Unable to find DpId from ACL interface with id {} and lport {}", portAfter.getInterfaceId(),
163 portAfter.getLPortTag());
166 boolean result = true;
167 boolean isPortSecurityEnable = portAfter.isPortSecurityEnabled();
168 boolean isPortSecurityEnableBefore = portBefore.isPortSecurityEnabled();
169 // if port security is changed, apply/remove Acls
170 if (isPortSecurityEnableBefore != isPortSecurityEnable) {
171 LOG.debug("On ACL update, Port security is {} for {}", isPortSecurityEnable ? "Enabled" :
172 "Disabled", portAfter.getInterfaceId());
173 if (isPortSecurityEnable) {
174 result = applyAcl(portAfter);
176 result = removeAcl(portBefore);
178 } else if (isPortSecurityEnable) {
179 // Acls has been updated, find added/removed Acls and act accordingly.
180 processInterfaceUpdate(portBefore, portAfter);
181 LOG.debug("On ACL update, ACL has been updated for {}", portAfter.getInterfaceId());
187 private void processInterfaceUpdate(AclInterface portBefore, AclInterface portAfter) {
188 List<AllowedAddressPairs> addedAaps = AclServiceUtils
189 .getUpdatedAllowedAddressPairs(portAfter.getAllowedAddressPairs(), portBefore.getAllowedAddressPairs());
190 List<AllowedAddressPairs> deletedAaps = AclServiceUtils
191 .getUpdatedAllowedAddressPairs(portBefore.getAllowedAddressPairs(), portAfter.getAllowedAddressPairs());
192 if (deletedAaps != null && !deletedAaps.isEmpty()) {
193 programAclWithAllowedAddress(portBefore, deletedAaps, Action.UPDATE, NwConstants.DEL_FLOW);
194 updateRemoteAclFilterTable(portBefore, portBefore.getSecurityGroups(), deletedAaps, NwConstants.DEL_FLOW);
196 if (addedAaps != null && !addedAaps.isEmpty()) {
197 programAclWithAllowedAddress(portAfter, addedAaps, Action.UPDATE, NwConstants.ADD_FLOW);
198 updateRemoteAclFilterTable(portAfter, portAfter.getSecurityGroups(), addedAaps, NwConstants.ADD_FLOW);
200 if (portAfter.getSubnetIpPrefixes() != null && portBefore.getSubnetIpPrefixes() == null) {
201 programBroadcastRules(portAfter, NwConstants.ADD_FLOW);
204 List<Uuid> addedAcls = AclServiceUtils.getUpdatedAclList(portAfter.getSecurityGroups(),
205 portBefore.getSecurityGroups());
206 List<Uuid> deletedAcls = AclServiceUtils.getUpdatedAclList(portBefore.getSecurityGroups(),
207 portAfter.getSecurityGroups());
208 if (deletedAcls.isEmpty() && addedAcls.isEmpty()) {
209 LOG.trace("No change w.r.t ACL list for port={}", portAfter.getInterfaceId());
213 handleAclChange(portBefore, deletedAcls, NwConstants.DEL_FLOW);
214 handleAclChange(portAfter, addedAcls, NwConstants.ADD_FLOW);
217 private void handleAclChange(AclInterface port, List<Uuid> aclList, int addOrRemove) {
218 int operationForAclRules = (addOrRemove == NwConstants.DEL_FLOW) ? NwConstants.MOD_FLOW : addOrRemove;
219 programAclRules(port, aclList, operationForAclRules);
220 updateRemoteAclFilterTable(port, aclList, port.getAllowedAddressPairs(), addOrRemove);
221 programAclDispatcherTable(port, addOrRemove);
224 protected SortedSet<Integer> getRemoteAclTags(AclInterface port) {
225 return this.direction == DirectionIngress.class ? port.getIngressRemoteAclTags()
226 : port.getEgressRemoteAclTags();
229 protected void programAclDispatcherTable(AclInterface port, int addOrRemove) {
230 SortedSet<Integer> remoteAclTags = getRemoteAclTags(port);
231 if (remoteAclTags.isEmpty()) {
232 LOG.debug("No {} rules with remote group id for port={}", this.directionString, port.getInterfaceId());
235 Integer firstRemoteAclTag = remoteAclTags.first();
236 Integer lastRemoteAclTag = remoteAclTags.last();
238 programFirstRemoteAclEntryInDispatcherTable(port, firstRemoteAclTag, addOrRemove);
239 programLastRemoteAclEntryInDispatcherTable(port, lastRemoteAclTag, addOrRemove);
241 Integer previousRemoteAclTag = firstRemoteAclTag;
242 for (Integer remoteAclTag : remoteAclTags) {
243 if (remoteAclTag.equals(firstRemoteAclTag)) {
246 List<MatchInfoBase> matches = new ArrayList<>();
247 matches.addAll(AclServiceUtils.buildMatchesForLPortTagAndRemoteAclTag(port.getLPortTag(),
248 previousRemoteAclTag, serviceMode));
249 String flowId = this.directionString + "_ACL_Dispatcher_" + port.getDpId() + "_" + port.getLPortTag() + "_"
252 List<InstructionInfo> instructions =
253 AclServiceOFFlowBuilder.getGotoInstructionInfo(getAclRuleBasedFilterTable());
254 instructions.add(AclServiceUtils.getWriteMetadataForRemoteAclTag(remoteAclTag));
255 syncFlow(port.getDpId(), getAclFilterCumDispatcherTable(), flowId,
256 AclConstants.ACE_GOTO_NEXT_REMOTE_ACL_PRIORITY, "ACL", 0, 0, AclConstants.COOKIE_ACL_BASE, matches,
257 instructions, addOrRemove);
259 previousRemoteAclTag = remoteAclTag;
263 protected void programFirstRemoteAclEntryInDispatcherTable(AclInterface port, Integer firstRemoteAclTag,
265 List<MatchInfoBase> matches = new ArrayList<>();
266 matches.add(AclServiceUtils.buildLPortTagMatch(port.getLPortTag(), serviceMode));
267 String flowId = this.directionString + "_ACL_Dispatcher_First_" + port.getDpId() + "_" + port.getLPortTag()
268 + "_" + firstRemoteAclTag;
270 List<InstructionInfo> instructions =
271 AclServiceOFFlowBuilder.getGotoInstructionInfo(getAclRuleBasedFilterTable());
272 instructions.add(AclServiceUtils.getWriteMetadataForRemoteAclTag(firstRemoteAclTag));
273 syncFlow(port.getDpId(), getAclFilterCumDispatcherTable(), flowId, AclConstants.ACE_FIRST_REMOTE_ACL_PRIORITY,
274 "ACL", 0, 0, AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
277 protected void programLastRemoteAclEntryInDispatcherTable(AclInterface port, Integer lastRemoteAclTag,
279 List<MatchInfoBase> matches = new ArrayList<>();
280 matches.addAll(AclServiceUtils.buildMatchesForLPortTagAndRemoteAclTag(port.getLPortTag(), lastRemoteAclTag,
282 String flowId = this.directionString + "_ACL_Dispatcher_Last_" + port.getDpId() + "_" + port.getLPortTag() + "_"
285 List<InstructionInfo> instructions = AclServiceOFFlowBuilder.getDropInstructionInfo();
286 syncFlow(port.getDpId(), getAclFilterCumDispatcherTable(), flowId, AclConstants.ACE_LAST_REMOTE_ACL_PRIORITY,
287 "ACL", 0, 0, AclConstants.COOKIE_ACL_DROP_FLOW, matches, instructions, addOrRemove);
290 private void programAcl(AclInterface port, Action action, int addOrRemove) {
291 programAclWithAllowedAddress(port, port.getAllowedAddressPairs(), action, addOrRemove);
294 private void programAclWithAllowedAddress(AclInterface port, List<AllowedAddressPairs> allowedAddresses,
295 Action action, int addOrRemove) {
296 BigInteger dpId = port.getDpId();
297 int lportTag = port.getLPortTag();
298 LOG.debug("Applying ACL Allowed Address on DpId {}, lportTag {}, Action {}", dpId, lportTag, action);
299 String portId = port.getInterfaceId();
300 programAntiSpoofingRules(port, allowedAddresses, action, addOrRemove);
301 programAclPortSpecificFixedRules(dpId, allowedAddresses, lportTag, portId, action, addOrRemove);
302 if (action == Action.ADD || action == Action.REMOVE) {
303 programAclRules(port, port.getSecurityGroups(), addOrRemove);
304 programAclDispatcherTable(port, addOrRemove);
309 * Programs the acl custom rules.
311 * @param port acl interface
312 * @param aclUuidList the list of acl uuid to be applied
313 * @param addOrRemove whether to delete or add flow
314 * @return program succeeded
316 protected boolean programAclRules(AclInterface port, List<Uuid> aclUuidList, int addOrRemove) {
317 BigInteger dpId = port.getDpId();
318 LOG.debug("Applying custom rules on DpId {}, lportTag {}", dpId, port.getLPortTag());
319 if (aclUuidList == null || dpId == null) {
320 LOG.warn("{} ACL parameters can not be null. dpId={}, aclUuidList={}", this.directionString, dpId,
324 for (Uuid aclUuid : aclUuidList) {
325 Acl acl = this.aclDataUtil.getAcl(aclUuid.getValue());
327 LOG.warn("The ACL {} not found in cache", aclUuid.getValue());
330 AccessListEntries accessListEntries = acl.getAccessListEntries();
331 List<Ace> aceList = accessListEntries.getAce();
332 for (Ace ace: aceList) {
333 programAceRule(port, aclUuid.getValue(), ace, addOrRemove);
340 * Programs the ace specific rule.
342 * @param port acl interface
343 * @param aclName the acl name
344 * @param ace rule to be program
345 * @param addOrRemove whether to delete or add flow
347 protected void programAceRule(AclInterface port, String aclName, Ace ace, int addOrRemove) {
348 SecurityRuleAttr aceAttr = AclServiceUtils.getAccesssListAttributes(ace);
349 if (!isValidDirection(aceAttr.getDirection())) {
350 LOG.trace("Ignoring {} direction while processing for {} ACE Rule {}", aceAttr.getDirection(),
351 this.directionString, ace.getRuleName());
354 LOG.debug("Program {} ACE rule for dpId={}, lportTag={}, addOrRemove={}, ace={}, portId={}",
355 this.directionString, port.getDpId(), port.getLPortTag(), addOrRemove, ace.getRuleName(),
356 port.getInterfaceId());
358 Matches matches = ace.getMatches();
359 Map<String, List<MatchInfoBase>> flowMap = null;
360 if (matches.getAceType() instanceof AceIp) {
361 flowMap = AclServiceOFFlowBuilder.programIpFlow(matches);
362 if (!AclServiceUtils.doesAceHaveRemoteGroupId(aceAttr)) {
363 // programming for ACE which doesn't have any remote group Id
364 programForAceNotHavingRemoteAclId(port, aclName, ace, flowMap, addOrRemove);
366 Uuid remoteAclId = aceAttr.getRemoteGroupId();
367 // programming for ACE which have remote group Id
368 programAceSpecificFlows(port, aclName, ace, flowMap, remoteAclId, addOrRemove);
373 protected void programForAceNotHavingRemoteAclId(AclInterface port, String aclName, Ace ace,
374 Map<String, List<MatchInfoBase>> flowMap, int addOrRemove) {
375 if (null == flowMap) {
378 MatchInfoBase lportTagMatch = AclServiceUtils.buildLPortTagMatch(port.getLPortTag(), serviceMode);
379 List<InstructionInfo> instructions = AclServiceOFFlowBuilder.getGotoInstructionInfo(getAclCommitterTable());
380 Integer flowPriority = this.aclServiceUtils.getAceFlowPriority(aclName);
382 for (Entry<String, List<MatchInfoBase>> entry : flowMap.entrySet()) {
383 String flowName = entry.getKey();
384 List<MatchInfoBase> matches = entry.getValue();
385 matches.add(lportTagMatch);
386 String flowId = flowName + this.directionString + "_" + port.getDpId() + "_" + port.getLPortTag() + "_"
387 + ace.getKey().getRuleName();
389 int operation = addOrRemove == NwConstants.MOD_FLOW ? NwConstants.DEL_FLOW : addOrRemove;
390 syncFlow(port.getDpId(), getAclFilterCumDispatcherTable(), flowId, flowPriority, "ACL", 0, 0,
391 AclConstants.COOKIE_ACL_BASE, matches, instructions, operation);
393 if (addOrRemove != NwConstants.DEL_FLOW) {
394 programAclForExistingTrafficTable(port, ace, addOrRemove, flowName, matches, flowPriority);
399 protected void programAceSpecificFlows(AclInterface port, String aclName, Ace ace,
400 Map<String, List<MatchInfoBase>> flowMap, Uuid remoteAclId, int addOrRemove) {
401 if (null == flowMap) {
404 Integer remoteAclTag = this.aclServiceUtils.getAclTag(remoteAclId);
405 if (remoteAclTag == null || remoteAclTag == AclConstants.INVALID_ACL_TAG) {
406 LOG.error("remoteAclTag=[] is null or invalid for remoteAclId={}", remoteAclTag, remoteAclId);
409 List<MatchInfoBase> lportAndAclMatches =
410 AclServiceUtils.buildMatchesForLPortTagAndRemoteAclTag(port.getLPortTag(), remoteAclTag, serviceMode);
411 List<InstructionInfo> instructions = AclServiceOFFlowBuilder.getGotoInstructionInfo(getAclRemoteAclTable());
412 Integer flowPriority = this.aclServiceUtils.getAceFlowPriority(aclName);
414 for (Entry<String, List<MatchInfoBase>> entry : flowMap.entrySet()) {
415 String flowName = entry.getKey();
416 List<MatchInfoBase> matches = entry.getValue();
417 matches.addAll(lportAndAclMatches);
418 String flowId = flowName + this.directionString + "_" + port.getDpId() + "_" + port.getLPortTag() + "_"
419 + ace.getKey().getRuleName();
421 int operation = addOrRemove == NwConstants.MOD_FLOW ? NwConstants.DEL_FLOW : addOrRemove;
422 syncFlow(port.getDpId(), getAclRuleBasedFilterTable(), flowId, flowPriority, "ACL", 0, 0,
423 AclConstants.COOKIE_ACL_BASE, matches, instructions, operation);
425 if (addOrRemove != NwConstants.DEL_FLOW) {
426 programAclForExistingTrafficTable(port, ace, addOrRemove, flowName, matches,
432 private void programAclForExistingTrafficTable(AclInterface port, Ace ace, int addOrRemove, String flowName,
433 List<MatchInfoBase> matches, Integer priority) {
434 AceIp acl = (AceIp) ace.getMatches().getAceType();
435 final String newFlowName = flowName + this.directionString + "_" + port.getDpId() + "_" + port.getLPortTag()
436 + "_" + ((acl.getAceIpVersion() instanceof AceIpv4) ? "_IPv4" : "_IPv6") + "_FlowAfterRuleDeleted";
438 final List<MatchInfoBase> newMatches =
439 matches.stream().filter(obj -> !(obj instanceof NxMatchCtState || obj instanceof MatchMetadata))
440 .collect(Collectors.toList());
441 newMatches.add(AclServiceUtils.buildLPortTagMatch(port.getLPortTag(), serviceMode));
442 newMatches.add(new NxMatchCtState(AclConstants.TRACKED_RPL_CT_STATE, AclConstants.TRACKED_RPL_CT_STATE_MASK));
444 List<InstructionInfo> instructions =
445 AclServiceUtils.createCtMarkInstructionForNewState(getAclFilterCumDispatcherTable(), port.getElanId());
446 // Reversing the flow add/delete operation for this table.
447 int operation = (addOrRemove == NwConstants.ADD_FLOW) ? NwConstants.DEL_FLOW : NwConstants.ADD_FLOW;
448 syncFlow(port.getDpId(), getAclForExistingTrafficTable(), newFlowName, priority, "ACL", 0,
449 AclServiceUtils.getHardTimoutForApplyStatefulChangeOnExistingTraffic(ace, aclServiceUtils),
450 AclConstants.COOKIE_ACL_BASE, newMatches, instructions, operation);
454 public boolean removeAcl(AclInterface port) {
455 if (port.getDpId() == null) {
456 LOG.warn("Unable to find DP Id from ACL interface with id {}", port.getInterfaceId());
459 programAcl(port, Action.REMOVE, NwConstants.DEL_FLOW);
460 updateRemoteAclFilterTable(port, NwConstants.DEL_FLOW);
465 public boolean applyAce(AclInterface port, String aclName, Ace ace) {
466 if (!port.isPortSecurityEnabled() || port.getDpId() == null) {
469 programAceRule(port, aclName, ace, NwConstants.ADD_FLOW);
474 public boolean removeAce(AclInterface port, String aclName, Ace ace) {
475 if (!port.isPortSecurityEnabled() || port.getDpId() == null) {
478 programAceRule(port, aclName, ace, NwConstants.MOD_FLOW);
483 public void updateRemoteAcl(Acl aclBefore, Acl aclAfter, Collection<AclInterface> portsBefore) {
484 handleRemoteAclUpdate(aclBefore, aclAfter, portsBefore);
490 * @param aclInterface the acl interface
492 public abstract void bindService(AclInterface aclInterface);
497 * @param aclInterface the acl interface
499 protected abstract void unbindService(AclInterface aclInterface);
502 * Programs the anti-spoofing rules.
504 * @param port the acl interface
505 * @param allowedAddresses the allowed addresses
506 * @param action add/modify/remove action
507 * @param addOrRemove addorRemove
509 protected abstract void programAntiSpoofingRules(AclInterface port, List<AllowedAddressPairs> allowedAddresses,
510 Action action, int addOrRemove);
513 * Programs broadcast rules.
515 * @param port the Acl Interface port
516 * @param addOrRemove whether to delete or add flow
518 protected abstract void programBroadcastRules(AclInterface port, int addOrRemove);
521 * Writes/remove the flow to/from the datastore.
540 * the list of matches to be writted
541 * @param instructions
542 * the list of instruction to be written.
544 * add or remove the entries.
546 protected void syncFlow(BigInteger dpId, short tableId, String flowId, int priority, String flowName,
547 int idleTimeOut, int hardTimeOut, BigInteger cookie, List<? extends MatchInfoBase> matches,
548 List<InstructionInfo> instructions, int addOrRemove) {
549 jobCoordinator.enqueueJob(flowName, () -> {
550 if (addOrRemove == NwConstants.DEL_FLOW) {
551 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, tableId, flowId, priority, flowName,
552 idleTimeOut, hardTimeOut, cookie, matches, null);
553 LOG.trace("Removing Acl Flow DpnId {}, flowId {}", dpId, flowId);
555 return Collections.singletonList(mdsalManager.removeFlow(dpId, flowEntity));
558 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, tableId, flowId, priority, flowName,
559 idleTimeOut, hardTimeOut, cookie, matches, instructions);
560 LOG.trace("Installing DpnId {}, flowId {}", dpId, flowId);
561 return Collections.singletonList(mdsalManager.installFlow(dpId, flowEntity));
566 protected List<InstructionInfo> getDispatcherTableResubmitInstructions() {
567 return getDispatcherTableResubmitInstructions(new ArrayList<>());
571 * Gets the dispatcher table resubmit instructions based on ingress/egress service mode w.r.t switch.
573 * @param actionsInfos
575 * @return the instructions for dispatcher table resubmit
577 protected List<InstructionInfo> getDispatcherTableResubmitInstructions(List<ActionInfo> actionsInfos) {
578 short dispatcherTableId = NwConstants.LPORT_DISPATCHER_TABLE;
579 if (ServiceModeEgress.class.equals(this.serviceMode)) {
580 dispatcherTableId = NwConstants.EGRESS_LPORT_DISPATCHER_TABLE;
583 List<InstructionInfo> instructions = new ArrayList<>();
584 actionsInfos.add(new ActionNxResubmit(dispatcherTableId));
585 instructions.add(new InstructionApplyActions(actionsInfos));
589 protected void handleRemoteAclUpdate(Acl aclBefore, Acl aclAfter, Collection<AclInterface> portsBefore) {
590 String aclName = aclAfter.getAclName();
591 Collection<AclInterface> interfaceList = aclDataUtil.getInterfaceList(new Uuid(aclName));
592 if (interfaceList == null || interfaceList.isEmpty()) {
593 LOG.trace("handleRemoteAclUpdate: No interfaces found with ACL={}", aclName);
596 Set<Uuid> remoteAclsBefore = AclServiceUtils.getRemoteAclIdsByDirection(aclBefore, this.direction);
597 Set<Uuid> remoteAclsAfter = AclServiceUtils.getRemoteAclIdsByDirection(aclAfter, this.direction);
599 Set<Uuid> remoteAclsAdded = new HashSet<>(remoteAclsAfter);
600 remoteAclsAdded.removeAll(remoteAclsBefore);
602 Set<Uuid> remoteAclsDeleted = new HashSet<>(remoteAclsBefore);
603 remoteAclsDeleted.removeAll(remoteAclsAfter);
605 if (!remoteAclsAdded.isEmpty() || !remoteAclsDeleted.isEmpty()) {
606 // delete and add flows in ACL dispatcher table for all applicable
608 for (AclInterface portBefore : portsBefore) {
609 programAclDispatcherTable(portBefore, NwConstants.DEL_FLOW);
611 for (AclInterface port : interfaceList) {
612 programAclDispatcherTable(port, NwConstants.ADD_FLOW);
615 Set<BigInteger> dpns = interfaceList.stream().map(port -> port.getDpId()).collect(Collectors.toSet());
617 programRemoteAclTable(aclName, remoteAclsDeleted, dpns, NwConstants.DEL_FLOW);
618 programRemoteAclTable(aclName, remoteAclsAdded, dpns, NwConstants.ADD_FLOW);
621 private void programRemoteAclTable(String aclName, Set<Uuid> remoteAclIds, Set<BigInteger> dpns, int addOrRemove) {
622 for (Uuid remoteAclId : remoteAclIds) {
623 Collection<AclInterface> remoteAclInterfaces = aclDataUtil.getInterfaceList(remoteAclId);
624 if (remoteAclInterfaces == null || remoteAclInterfaces.isEmpty()) {
627 Set<AllowedAddressPairs> aaps =
628 remoteAclInterfaces.stream().map(port -> port.getAllowedAddressPairs()).flatMap(List::stream)
629 .filter(aap -> AclServiceUtils.isNotIpAllNetwork(aap)).collect(Collectors.toSet());
631 Integer aclTag = aclServiceUtils.getAclTag(remoteAclId);
632 if (addOrRemove == NwConstants.ADD_FLOW) {
633 for (BigInteger dpn : dpns) {
634 for (AllowedAddressPairs aap : aaps) {
635 programRemoteAclTableFlow(dpn, aclTag, aap, addOrRemove);
638 } else if (addOrRemove == NwConstants.DEL_FLOW) {
639 Set<BigInteger> remoteAclDpns = new HashSet<>();
640 Map<String, Set<AclInterface>> mapAclWithPortSet =
641 aclDataUtil.getRemoteAclInterfaces(remoteAclId, this.direction);
642 if (mapAclWithPortSet != null) {
643 Map<String, Set<AclInterface>> copyOfMapAclWithPortSet = new HashMap<>(mapAclWithPortSet);
644 copyOfMapAclWithPortSet.remove(aclName);
645 remoteAclDpns = collectDpns(copyOfMapAclWithPortSet);
647 Set<BigInteger> dpnsToOperate = new HashSet<>(dpns);
648 dpnsToOperate.removeAll(remoteAclDpns);
650 "Deleting flows in Remote ACL table for remoteAclId={}, direction={}, dpnsToOperate={}, "
651 + "remoteAclDpns={}, dpns={}",
652 remoteAclId.getValue(), directionString, dpnsToOperate, remoteAclDpns, dpns);
654 for (BigInteger dpn : dpnsToOperate) {
655 for (AllowedAddressPairs aap : aaps) {
656 programRemoteAclTableFlow(dpn, aclTag, aap, addOrRemove);
663 private void updateRemoteAclFilterTable(AclInterface port, int addOrRemove) {
664 updateRemoteAclFilterTable(port, port.getSecurityGroups(), port.getAllowedAddressPairs(), addOrRemove);
667 private void updateRemoteAclFilterTable(AclInterface port, List<Uuid> aclList, List<AllowedAddressPairs> aaps,
669 if (aclList == null) {
670 LOG.debug("Port {} without SGs", port.getInterfaceId());
673 String portId = port.getInterfaceId();
674 LOG.trace("updateRemoteAclFilterTable for portId={}, aclList={}, aaps={}, addOrRemove={}", portId, aclList,
676 for (Uuid aclId : aclList) {
677 if (aclDataUtil.getRemoteAcl(aclId, this.direction) != null) {
678 Integer aclTag = aclServiceUtils.getAclTag(aclId);
679 if (addOrRemove == NwConstants.ADD_FLOW) {
680 syncRemoteAclTable(portId, aclId, aclTag, aaps, addOrRemove);
681 } else if (addOrRemove == NwConstants.DEL_FLOW) {
682 // Synchronizing during delete operation as there are
683 // look-ups for AclPortsLookup data.
684 synchronized (aclId.getValue().intern()) {
685 syncRemoteAclTable(portId, aclId, aclTag, aaps, addOrRemove);
690 Set<Uuid> remoteAclIds = aclServiceUtils.getRemoteAclIdsByDirection(aclList, direction);
691 for (Uuid remoteAclId : remoteAclIds) {
692 syncRemoteAclTableFromOtherDpns(port, remoteAclId, addOrRemove);
696 private void syncRemoteAclTable(String portId, Uuid acl, Integer aclTag, List<AllowedAddressPairs> aaps,
698 Map<String, Set<AclInterface>> mapAclWithPortSet = aclDataUtil.getRemoteAclInterfaces(acl, this.direction);
699 Set<BigInteger> dpns = collectDpns(mapAclWithPortSet);
700 for (AllowedAddressPairs aap : aaps) {
701 if (!AclServiceUtils.isNotIpAllNetwork(aap)) {
704 if (aclServiceUtils.skipDeleteInCaseOfOverlappingIP(portId, acl, aap.getIpAddress(),
706 LOG.debug("Skipping delete of IP={} in remote ACL table for remoteAclId={}, portId={}",
707 aap.getIpAddress(), portId, acl.getValue());
710 for (BigInteger dpId : dpns) {
711 programRemoteAclTableFlow(dpId, aclTag, aap, addOrRemove);
716 private void syncRemoteAclTableFromOtherDpns(AclInterface port, Uuid remoteAclId, int addOrRemove) {
717 Collection<AclInterface> aclInterfaces = aclDataUtil.getInterfaceList(remoteAclId);
719 if (aclInterfaces != null && !aclInterfaces.isEmpty() && isFirstPortInDpnWithRemoteAclId(port, remoteAclId)) {
720 Integer aclTag = aclServiceUtils.getAclTag(remoteAclId);
721 for (AclInterface aclInterface : aclInterfaces) {
722 if (port.getInterfaceId().equals(aclInterface.getInterfaceId())) {
725 for (AllowedAddressPairs aap : aclInterface.getAllowedAddressPairs()) {
726 if (AclServiceUtils.isNotIpAllNetwork(aap)) {
727 programRemoteAclTableFlow(port.getDpId(), aclTag, aap, addOrRemove);
734 private boolean isFirstPortInDpnWithRemoteAclId(AclInterface port, Uuid remoteAclId) {
735 String portId = port.getInterfaceId();
736 BigInteger dpId = port.getDpId();
737 Map<String, Set<AclInterface>> remoteAclInterfacesMap =
738 aclDataUtil.getRemoteAclInterfaces(remoteAclId, direction);
739 if (remoteAclInterfacesMap != null) {
740 for (Set<AclInterface> interfaceSet : remoteAclInterfacesMap.values()) {
741 for (AclInterface aclInterface : interfaceSet) {
742 if (portId.equals(aclInterface.getInterfaceId())) {
745 if (dpId.equals(aclInterface.getDpId())) {
754 protected abstract void programRemoteAclTableFlow(BigInteger dpId, Integer aclTag, AllowedAddressPairs aap,
757 protected String getOperAsString(int flowOper) {
760 case NwConstants.ADD_FLOW:
763 case NwConstants.DEL_FLOW:
766 case NwConstants.MOD_FLOW:
775 protected Set<BigInteger> collectDpns(Map<String, Set<AclInterface>> mapAclWithPortSet) {
776 Set<BigInteger> dpns = new HashSet<>();
777 if (mapAclWithPortSet == null) {
780 for (Set<AclInterface> innerSet : mapAclWithPortSet.values()) {
781 if (innerSet == null) {
784 for (AclInterface inter : innerSet) {
785 dpns.add(inter.getDpId());
792 * Programs the port specific fixed rules.
794 * @param dpId the dp id
795 * @param allowedAddresses the allowed addresses
796 * @param lportTag the lport tag
797 * @param portId the portId
798 * @param action the action
799 * @param write whether to add or remove the flow.
801 protected void programAclPortSpecificFixedRules(BigInteger dpId, List<AllowedAddressPairs> allowedAddresses,
802 int lportTag, String portId, Action action, int write) {
803 programGotoClassifierTableRules(dpId, allowedAddresses, lportTag, write);
804 if (action == Action.ADD || action == Action.REMOVE) {
805 programConntrackRecircRules(dpId, allowedAddresses, lportTag, portId, write);
806 programPortSpecificDropRules(dpId, lportTag, write);
807 programAclCommitRules(dpId, lportTag, portId, write);
809 LOG.info("programAclPortSpecificFixedRules: flows for dpId={}, lportId={}, action={}, write={}", dpId, lportTag,
813 protected abstract void programGotoClassifierTableRules(BigInteger dpId, List<AllowedAddressPairs> aaps,
814 int lportTag, int addOrRemove);
817 * Adds the rule to send the packet to the netfilter to check whether it is a known packet.
819 * @param dpId the dpId
820 * @param aaps the allowed address pairs
821 * @param lportTag the lport tag
822 * @param portId the portId
823 * @param addOrRemove whether to add or remove the flow
825 protected void programConntrackRecircRules(BigInteger dpId, List<AllowedAddressPairs> aaps, int lportTag,
826 String portId, int addOrRemove) {
827 if (AclServiceUtils.doesIpv4AddressExists(aaps)) {
828 programConntrackRecircRule(dpId, lportTag, portId, MatchEthernetType.IPV4, addOrRemove);
830 if (AclServiceUtils.doesIpv6AddressExists(aaps)) {
831 programConntrackRecircRule(dpId, lportTag, portId, MatchEthernetType.IPV6, addOrRemove);
835 protected void programConntrackRecircRule(BigInteger dpId, int lportTag, String portId,
836 MatchEthernetType matchEtherType, int addOrRemove) {
837 List<MatchInfoBase> matches = new ArrayList<>();
838 matches.add(matchEtherType);
839 matches.add(AclServiceUtils.buildLPortTagMatch(lportTag, serviceMode));
841 List<InstructionInfo> instructions = new ArrayList<>();
842 if (addOrRemove == NwConstants.ADD_FLOW) {
843 Long elanTag = getElanIdFromAclInterface(portId);
844 if (elanTag == null) {
845 LOG.error("ElanId not found for portId={}; Context: dpId={}, lportTag={}, addOrRemove={},", portId,
846 dpId, lportTag, addOrRemove);
849 List<ActionInfo> actionsInfos = new ArrayList<>();
850 actionsInfos.add(new ActionNxConntrack(2, 0, 0, elanTag.intValue(), getAclForExistingTrafficTable()));
851 instructions.add(new InstructionApplyActions(actionsInfos));
855 this.directionString + "_Fixed_Conntrk_" + dpId + "_" + lportTag + "_" + matchEtherType + "_Recirc";
856 syncFlow(dpId, getAclConntrackSenderTable(), flowName, AclConstants.ACL_DEFAULT_PRIORITY, "ACL", 0, 0,
857 AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
861 * Adds the rules to drop the unknown/invalid packets .
863 * @param dpId the dpId
864 * @param lportTag the lport tag
865 * @param addOrRemove whether to add or remove the flow
867 protected void programPortSpecificDropRules(BigInteger dpId, int lportTag, int addOrRemove) {
868 LOG.debug("Programming Drop Rules: DpId={}, lportTag={}, addOrRemove={}", dpId, lportTag, addOrRemove);
869 programConntrackInvalidDropRule(dpId, lportTag, addOrRemove);
870 programAclRuleMissDropRule(dpId, lportTag, addOrRemove);
874 * Adds the rule to drop the conntrack invalid packets .
876 * @param dpId the dpId
877 * @param lportTag the lport tag
878 * @param addOrRemove whether to add or remove the flow
880 protected void programConntrackInvalidDropRule(BigInteger dpId, int lportTag, int addOrRemove) {
881 List<MatchInfoBase> matches = AclServiceOFFlowBuilder.addLPortTagMatches(lportTag,
882 AclConstants.TRACKED_INV_CT_STATE, AclConstants.TRACKED_INV_CT_STATE_MASK, serviceMode);
883 List<InstructionInfo> instructions = AclServiceOFFlowBuilder.getDropInstructionInfo();
885 String flowId = this.directionString + "_Fixed_Conntrk_Drop" + dpId + "_" + lportTag + "_Tracked_Invalid";
886 syncFlow(dpId, getAclFilterCumDispatcherTable(), flowId, AclConstants.CT_STATE_TRACKED_INVALID_PRIORITY, "ACL",
887 0, 0, AclConstants.COOKIE_ACL_DROP_FLOW, matches, instructions, addOrRemove);
891 * Program ACL rule miss drop rule for a port.
893 * @param dpId the dp id
894 * @param lportTag the lport tag
895 * @param addOrRemove the add or remove
897 protected void programAclRuleMissDropRule(BigInteger dpId, int lportTag, int addOrRemove) {
898 List<MatchInfoBase> matches = new ArrayList<>();
899 matches.add(AclServiceUtils.buildLPortTagMatch(lportTag, serviceMode));
900 List<InstructionInfo> instructions = AclServiceOFFlowBuilder.getDropInstructionInfo();
902 String flowId = this.directionString + "_Fixed_Acl_Rule_Miss_Drop_" + dpId + "_" + lportTag;
903 syncFlow(dpId, getAclFilterCumDispatcherTable(), flowId, AclConstants.ACL_PORT_SPECIFIC_DROP_PRIORITY, "ACL", 0,
904 0, AclConstants.COOKIE_ACL_DROP_FLOW, matches, instructions, addOrRemove);
908 * Program acl commit rules.
910 * @param dpId the dp id
911 * @param lportTag the lport tag
912 * @param portId the port id
913 * @param addOrRemove the add or remove
915 protected void programAclCommitRules(BigInteger dpId, int lportTag, String portId, int addOrRemove) {
916 programAclCommitRuleForConntrack(dpId, lportTag, portId, MatchEthernetType.IPV4, addOrRemove);
917 programAclCommitRuleForConntrack(dpId, lportTag, portId, MatchEthernetType.IPV6, addOrRemove);
918 programAclCommitRuleForNonConntrack(dpId, lportTag, addOrRemove);
922 * Program acl commit rule for conntrack.
924 * @param dpId the dp id
925 * @param lportTag the lport tag
926 * @param portId the port id
927 * @param matchEtherType the match ether type
928 * @param addOrRemove the add or remove
930 protected void programAclCommitRuleForConntrack(BigInteger dpId, int lportTag, String portId,
931 MatchEthernetType matchEtherType, int addOrRemove) {
932 List<MatchInfoBase> matches = new ArrayList<>();
933 matches.add(matchEtherType);
934 matches.addAll(AclServiceUtils.buildMatchesForLPortTagAndConntrackClassifierType(lportTag,
935 AclConntrackClassifierType.CONNTRACK_SUPPORTED, serviceMode));
937 List<ActionInfo> actionsInfos = new ArrayList<>();
938 if (addOrRemove == NwConstants.ADD_FLOW) {
939 Long elanId = getElanIdFromAclInterface(portId);
940 if (elanId == null) {
941 LOG.error("ElanId not found for portId={}; Context: dpId={}, lportTag={}, addOrRemove={}", portId, dpId,
942 lportTag, addOrRemove);
945 List<NxCtAction> ctActionsList =
946 Lists.newArrayList(new ActionNxConntrack.NxCtMark(AclConstants.CT_MARK_EST_STATE));
947 actionsInfos.add(new ActionNxConntrack(2, 1, 0, elanId.intValue(), (short) 255, ctActionsList));
949 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions(actionsInfos);
951 String flowName = directionString + "_Acl_Commit_Conntrack_" + dpId + "_" + lportTag + "_" + matchEtherType;
952 // Flow for conntrack traffic to commit and resubmit to dispatcher
953 syncFlow(dpId, getAclCommitterTable(), flowName, AclConstants.ACL_DEFAULT_PRIORITY, "ACL", 0, 0,
954 AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
958 * Program acl commit rule for non conntrack.
960 * @param dpId the dp id
961 * @param lportTag the lport tag
962 * @param addOrRemove the add or remove
964 protected void programAclCommitRuleForNonConntrack(BigInteger dpId, int lportTag, int addOrRemove) {
965 List<MatchInfoBase> matches = new ArrayList<>();
966 matches.addAll(AclServiceUtils.buildMatchesForLPortTagAndConntrackClassifierType(lportTag,
967 AclConntrackClassifierType.NON_CONNTRACK_SUPPORTED, serviceMode));
969 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
970 String flowName = this.directionString + "_Acl_Commit_Non_Conntrack_" + dpId + "_" + lportTag;
971 // Flow for non-conntrack traffic to resubmit to dispatcher
972 syncFlow(dpId, getAclCommitterTable(), flowName, AclConstants.ACL_DEFAULT_PRIORITY, "ACL", 0, 0,
973 AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
976 protected Long getElanIdFromAclInterface(String elanInterfaceName) {
977 AclInterface aclInterface = aclInterfaceCache.get(elanInterfaceName);
978 if (null != aclInterface) {
979 return aclInterface.getElanId();
984 protected abstract boolean isValidDirection(Class<? extends DirectionBase> direction);
986 protected abstract short getAclAntiSpoofingTable();
988 protected abstract short getAclConntrackClassifierTable();
990 protected abstract short getAclConntrackSenderTable();
992 protected abstract short getAclForExistingTrafficTable();
994 protected abstract short getAclFilterCumDispatcherTable();
996 protected abstract short getAclRuleBasedFilterTable();
998 protected abstract short getAclRemoteAclTable();
1000 protected abstract short getAclCommitterTable();