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);
688 syncRemoteAclTableFromOtherDpns(port, aclId, aclTag, addOrRemove);
693 private void syncRemoteAclTable(String portId, Uuid acl, Integer aclTag, List<AllowedAddressPairs> aaps,
695 Map<String, Set<AclInterface>> mapAclWithPortSet = aclDataUtil.getRemoteAclInterfaces(acl, this.direction);
696 Set<BigInteger> dpns = collectDpns(mapAclWithPortSet);
697 for (AllowedAddressPairs aap : aaps) {
698 if (!AclServiceUtils.isNotIpAllNetwork(aap)) {
701 if (aclServiceUtils.skipDeleteInCaseOfOverlappingIP(portId, acl, aap.getIpAddress(),
703 LOG.debug("Skipping delete of IP={} in remote ACL table for remoteAclId={}, portId={}",
704 aap.getIpAddress(), portId, acl.getValue());
707 for (BigInteger dpId : dpns) {
708 programRemoteAclTableFlow(dpId, aclTag, aap, addOrRemove);
713 private void syncRemoteAclTableFromOtherDpns(AclInterface port, Uuid acl, Integer aclTag, int addOrRemove) {
714 Collection<AclInterface> aclInterfaces = aclDataUtil.getInterfaceList(acl);
715 BigInteger dpId = port.getDpId();
716 boolean isFirstPortInDpn = true;
717 if (aclInterfaces != null) {
718 for (AclInterface aclInterface : aclInterfaces) {
719 if (port.getInterfaceId().equals(aclInterface.getInterfaceId())) {
722 if (dpId.equals(aclInterface.getDpId())) {
723 isFirstPortInDpn = false;
727 if (isFirstPortInDpn) {
728 for (AclInterface aclInterface : aclInterfaces) {
729 if (port.getInterfaceId().equals(aclInterface.getInterfaceId())) {
732 for (AllowedAddressPairs aap : aclInterface.getAllowedAddressPairs()) {
733 if (AclServiceUtils.isNotIpAllNetwork(aap)) {
734 programRemoteAclTableFlow(port.getDpId(), aclTag, aap, addOrRemove);
742 protected abstract void programRemoteAclTableFlow(BigInteger dpId, Integer aclTag, AllowedAddressPairs aap,
745 protected String getOperAsString(int flowOper) {
748 case NwConstants.ADD_FLOW:
751 case NwConstants.DEL_FLOW:
754 case NwConstants.MOD_FLOW:
763 protected Set<BigInteger> collectDpns(Map<String, Set<AclInterface>> mapAclWithPortSet) {
764 Set<BigInteger> dpns = new HashSet<>();
765 if (mapAclWithPortSet == null) {
768 for (Set<AclInterface> innerSet : mapAclWithPortSet.values()) {
769 if (innerSet == null) {
772 for (AclInterface inter : innerSet) {
773 dpns.add(inter.getDpId());
780 * Programs the port specific fixed rules.
782 * @param dpId the dp id
783 * @param allowedAddresses the allowed addresses
784 * @param lportTag the lport tag
785 * @param portId the portId
786 * @param action the action
787 * @param write whether to add or remove the flow.
789 protected void programAclPortSpecificFixedRules(BigInteger dpId, List<AllowedAddressPairs> allowedAddresses,
790 int lportTag, String portId, Action action, int write) {
791 programGotoClassifierTableRules(dpId, allowedAddresses, lportTag, write);
792 if (action == Action.ADD || action == Action.REMOVE) {
793 programConntrackRecircRules(dpId, allowedAddresses, lportTag, portId, write);
794 programPortSpecificDropRules(dpId, lportTag, write);
795 programAclCommitRules(dpId, lportTag, portId, write);
797 LOG.info("programAclPortSpecificFixedRules: flows for dpId={}, lportId={}, action={}, write={}", dpId, lportTag,
801 protected abstract void programGotoClassifierTableRules(BigInteger dpId, List<AllowedAddressPairs> aaps,
802 int lportTag, int addOrRemove);
805 * Adds the rule to send the packet to the netfilter to check whether it is a known packet.
807 * @param dpId the dpId
808 * @param aaps the allowed address pairs
809 * @param lportTag the lport tag
810 * @param portId the portId
811 * @param addOrRemove whether to add or remove the flow
813 protected void programConntrackRecircRules(BigInteger dpId, List<AllowedAddressPairs> aaps, int lportTag,
814 String portId, int addOrRemove) {
815 if (AclServiceUtils.doesIpv4AddressExists(aaps)) {
816 programConntrackRecircRule(dpId, lportTag, portId, MatchEthernetType.IPV4, addOrRemove);
818 if (AclServiceUtils.doesIpv6AddressExists(aaps)) {
819 programConntrackRecircRule(dpId, lportTag, portId, MatchEthernetType.IPV6, addOrRemove);
823 protected void programConntrackRecircRule(BigInteger dpId, int lportTag, String portId,
824 MatchEthernetType matchEtherType, int addOrRemove) {
825 List<MatchInfoBase> matches = new ArrayList<>();
826 matches.add(matchEtherType);
827 matches.add(AclServiceUtils.buildLPortTagMatch(lportTag, serviceMode));
829 List<InstructionInfo> instructions = new ArrayList<>();
830 if (addOrRemove == NwConstants.ADD_FLOW) {
831 Long elanTag = getElanIdFromAclInterface(portId);
832 if (elanTag == null) {
833 LOG.error("ElanId not found for portId={}; Context: dpId={}, lportTag={}, addOrRemove={},", portId,
834 dpId, lportTag, addOrRemove);
837 List<ActionInfo> actionsInfos = new ArrayList<>();
838 actionsInfos.add(new ActionNxConntrack(2, 0, 0, elanTag.intValue(), getAclForExistingTrafficTable()));
839 instructions.add(new InstructionApplyActions(actionsInfos));
843 this.directionString + "_Fixed_Conntrk_" + dpId + "_" + lportTag + "_" + matchEtherType + "_Recirc";
844 syncFlow(dpId, getAclConntrackSenderTable(), flowName, AclConstants.ACL_DEFAULT_PRIORITY, "ACL", 0, 0,
845 AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
849 * Adds the rules to drop the unknown/invalid packets .
851 * @param dpId the dpId
852 * @param lportTag the lport tag
853 * @param addOrRemove whether to add or remove the flow
855 protected void programPortSpecificDropRules(BigInteger dpId, int lportTag, int addOrRemove) {
856 LOG.debug("Programming Drop Rules: DpId={}, lportTag={}, addOrRemove={}", dpId, lportTag, addOrRemove);
857 programConntrackInvalidDropRule(dpId, lportTag, addOrRemove);
858 programAclRuleMissDropRule(dpId, lportTag, addOrRemove);
862 * Adds the rule to drop the conntrack invalid packets .
864 * @param dpId the dpId
865 * @param lportTag the lport tag
866 * @param addOrRemove whether to add or remove the flow
868 protected void programConntrackInvalidDropRule(BigInteger dpId, int lportTag, int addOrRemove) {
869 List<MatchInfoBase> matches = AclServiceOFFlowBuilder.addLPortTagMatches(lportTag,
870 AclConstants.TRACKED_INV_CT_STATE, AclConstants.TRACKED_INV_CT_STATE_MASK, serviceMode);
871 List<InstructionInfo> instructions = AclServiceOFFlowBuilder.getDropInstructionInfo();
873 String flowId = this.directionString + "_Fixed_Conntrk_Drop" + dpId + "_" + lportTag + "_Tracked_Invalid";
874 syncFlow(dpId, getAclFilterCumDispatcherTable(), flowId, AclConstants.CT_STATE_TRACKED_INVALID_PRIORITY, "ACL",
875 0, 0, AclConstants.COOKIE_ACL_DROP_FLOW, matches, instructions, addOrRemove);
879 * Program ACL rule miss drop rule for a port.
881 * @param dpId the dp id
882 * @param lportTag the lport tag
883 * @param addOrRemove the add or remove
885 protected void programAclRuleMissDropRule(BigInteger dpId, int lportTag, int addOrRemove) {
886 List<MatchInfoBase> matches = new ArrayList<>();
887 matches.add(AclServiceUtils.buildLPortTagMatch(lportTag, serviceMode));
888 List<InstructionInfo> instructions = AclServiceOFFlowBuilder.getDropInstructionInfo();
890 String flowId = this.directionString + "_Fixed_Acl_Rule_Miss_Drop_" + dpId + "_" + lportTag;
891 syncFlow(dpId, getAclFilterCumDispatcherTable(), flowId, AclConstants.ACL_PORT_SPECIFIC_DROP_PRIORITY, "ACL", 0,
892 0, AclConstants.COOKIE_ACL_DROP_FLOW, matches, instructions, addOrRemove);
896 * Program acl commit rules.
898 * @param dpId the dp id
899 * @param lportTag the lport tag
900 * @param portId the port id
901 * @param addOrRemove the add or remove
903 protected void programAclCommitRules(BigInteger dpId, int lportTag, String portId, int addOrRemove) {
904 programAclCommitRuleForConntrack(dpId, lportTag, portId, MatchEthernetType.IPV4, addOrRemove);
905 programAclCommitRuleForConntrack(dpId, lportTag, portId, MatchEthernetType.IPV6, addOrRemove);
906 programAclCommitRuleForNonConntrack(dpId, lportTag, addOrRemove);
910 * Program acl commit rule for conntrack.
912 * @param dpId the dp id
913 * @param lportTag the lport tag
914 * @param portId the port id
915 * @param matchEtherType the match ether type
916 * @param addOrRemove the add or remove
918 protected void programAclCommitRuleForConntrack(BigInteger dpId, int lportTag, String portId,
919 MatchEthernetType matchEtherType, int addOrRemove) {
920 List<MatchInfoBase> matches = new ArrayList<>();
921 matches.add(matchEtherType);
922 matches.addAll(AclServiceUtils.buildMatchesForLPortTagAndConntrackClassifierType(lportTag,
923 AclConntrackClassifierType.CONNTRACK_SUPPORTED, serviceMode));
925 List<ActionInfo> actionsInfos = new ArrayList<>();
926 if (addOrRemove == NwConstants.ADD_FLOW) {
927 Long elanId = getElanIdFromAclInterface(portId);
928 if (elanId == null) {
929 LOG.error("ElanId not found for portId={}; Context: dpId={}, lportTag={}, addOrRemove={}", portId, dpId,
930 lportTag, addOrRemove);
933 List<NxCtAction> ctActionsList =
934 Lists.newArrayList(new ActionNxConntrack.NxCtMark(AclConstants.CT_MARK_EST_STATE));
935 actionsInfos.add(new ActionNxConntrack(2, 1, 0, elanId.intValue(), (short) 255, ctActionsList));
937 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions(actionsInfos);
939 String flowName = directionString + "_Acl_Commit_Conntrack_" + dpId + "_" + lportTag + "_" + matchEtherType;
940 // Flow for conntrack traffic to commit and resubmit to dispatcher
941 syncFlow(dpId, getAclCommitterTable(), flowName, AclConstants.ACL_DEFAULT_PRIORITY, "ACL", 0, 0,
942 AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
946 * Program acl commit rule for non conntrack.
948 * @param dpId the dp id
949 * @param lportTag the lport tag
950 * @param addOrRemove the add or remove
952 protected void programAclCommitRuleForNonConntrack(BigInteger dpId, int lportTag, int addOrRemove) {
953 List<MatchInfoBase> matches = new ArrayList<>();
954 matches.addAll(AclServiceUtils.buildMatchesForLPortTagAndConntrackClassifierType(lportTag,
955 AclConntrackClassifierType.NON_CONNTRACK_SUPPORTED, serviceMode));
957 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
958 String flowName = this.directionString + "_Acl_Commit_Non_Conntrack_" + dpId + "_" + lportTag;
959 // Flow for non-conntrack traffic to resubmit to dispatcher
960 syncFlow(dpId, getAclCommitterTable(), flowName, AclConstants.ACL_DEFAULT_PRIORITY, "ACL", 0, 0,
961 AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
964 protected Long getElanIdFromAclInterface(String elanInterfaceName) {
965 AclInterface aclInterface = aclInterfaceCache.get(elanInterfaceName);
966 if (null != aclInterface) {
967 return aclInterface.getElanId();
972 protected abstract boolean isValidDirection(Class<? extends DirectionBase> direction);
974 protected abstract short getAclAntiSpoofingTable();
976 protected abstract short getAclConntrackClassifierTable();
978 protected abstract short getAclConntrackSenderTable();
980 protected abstract short getAclForExistingTrafficTable();
982 protected abstract short getAclFilterCumDispatcherTable();
984 protected abstract short getAclRuleBasedFilterTable();
986 protected abstract short getAclRemoteAclTable();
988 protected abstract short getAclCommitterTable();