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.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.port.subnets.port.subnet.SubnetInfo;
65 import org.slf4j.Logger;
66 import org.slf4j.LoggerFactory;
68 public abstract class AbstractAclServiceImpl implements AclServiceListener {
70 private static final Logger LOG = LoggerFactory.getLogger(AbstractAclServiceImpl.class);
72 protected final IMdsalApiManager mdsalManager;
73 protected final DataBroker dataBroker;
74 protected final ManagedNewTransactionRunner txRunner;
75 protected final Class<? extends ServiceModeBase> serviceMode;
76 protected final AclDataUtil aclDataUtil;
77 protected final AclServiceUtils aclServiceUtils;
78 protected final JobCoordinator jobCoordinator;
79 protected final AclInterfaceCache aclInterfaceCache;
81 protected final Class<? extends DirectionBase> direction;
82 protected final String directionString;
85 * Initialize the member variables.
87 * @param serviceMode the service mode
88 * @param dataBroker the data broker instance.
89 * @param mdsalManager the mdsal manager instance.
90 * @param aclDataUtil the acl data util.
91 * @param aclServiceUtils the acl service util.
92 * @param jobCoordinator the job coordinator
93 * @param aclInterfaceCache the acl interface cache
95 public AbstractAclServiceImpl(Class<? extends ServiceModeBase> serviceMode, DataBroker dataBroker,
96 IMdsalApiManager mdsalManager, AclDataUtil aclDataUtil, AclServiceUtils aclServiceUtils,
97 JobCoordinator jobCoordinator, AclInterfaceCache aclInterfaceCache) {
98 this.dataBroker = dataBroker;
99 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
100 this.mdsalManager = mdsalManager;
101 this.serviceMode = serviceMode;
102 this.aclDataUtil = aclDataUtil;
103 this.aclServiceUtils = aclServiceUtils;
104 this.jobCoordinator = jobCoordinator;
105 this.aclInterfaceCache = aclInterfaceCache;
108 this.serviceMode.equals(ServiceModeEgress.class) ? DirectionIngress.class : DirectionEgress.class;
109 this.directionString = this.direction.equals(DirectionEgress.class) ? "Egress" : "Ingress";
113 public boolean applyAcl(AclInterface port) {
115 LOG.error("port cannot be null");
118 if (port.getSecurityGroups() == null) {
119 LOG.info("Port {} without SGs", port.getInterfaceId());
122 BigInteger dpId = port.getDpId();
123 if (dpId == null || port.getLPortTag() == null) {
124 LOG.error("Unable to find DpId from ACL interface with id {}", port.getInterfaceId());
127 LOG.debug("Applying ACL on port {} with DpId {}", port, dpId);
128 programAcl(port, Action.ADD, NwConstants.ADD_FLOW);
129 updateRemoteAclFilterTable(port, NwConstants.ADD_FLOW);
134 public boolean bindAcl(AclInterface port) {
135 if (port == null || port.getSecurityGroups() == null) {
136 LOG.error("Port and port security groups cannot be null for binding ACL service, port={}", port);
140 if (port.getDpId() != null) {
141 updateRemoteAclFilterTable(port, NwConstants.ADD_FLOW);
147 public boolean unbindAcl(AclInterface port) {
149 LOG.error("Port cannot be null for unbinding ACL service");
152 if (port.getDpId() != null) {
154 updateRemoteAclFilterTable(port, NwConstants.DEL_FLOW);
160 public boolean updateAcl(AclInterface portBefore, AclInterface portAfter) {
161 // this check is to avoid situations of port update coming before interface state is up
162 if (portAfter.getDpId() == null || portAfter.getLPortTag() == null) {
163 LOG.debug("Unable to find DpId from ACL interface with id {} and lport {}", portAfter.getInterfaceId(),
164 portAfter.getLPortTag());
167 boolean result = true;
168 boolean isPortSecurityEnable = portAfter.isPortSecurityEnabled();
169 boolean isPortSecurityEnableBefore = portBefore.isPortSecurityEnabled();
170 // if port security is changed, apply/remove Acls
171 if (isPortSecurityEnableBefore != isPortSecurityEnable) {
172 LOG.debug("On ACL update, Port security is {} for {}", isPortSecurityEnable ? "Enabled" :
173 "Disabled", portAfter.getInterfaceId());
174 if (isPortSecurityEnable) {
175 result = applyAcl(portAfter);
177 result = removeAcl(portBefore);
179 } else if (isPortSecurityEnable) {
180 // Acls has been updated, find added/removed Acls and act accordingly.
181 processInterfaceUpdate(portBefore, portAfter);
182 LOG.debug("On ACL update, ACL has been updated for {}", portAfter.getInterfaceId());
188 private void processInterfaceUpdate(AclInterface portBefore, AclInterface portAfter) {
189 List<AllowedAddressPairs> addedAaps = AclServiceUtils
190 .getUpdatedAllowedAddressPairs(portAfter.getAllowedAddressPairs(), portBefore.getAllowedAddressPairs());
191 List<AllowedAddressPairs> deletedAaps = AclServiceUtils
192 .getUpdatedAllowedAddressPairs(portBefore.getAllowedAddressPairs(), portAfter.getAllowedAddressPairs());
193 if (deletedAaps != null && !deletedAaps.isEmpty()) {
194 programAclWithAllowedAddress(portBefore, deletedAaps, Action.UPDATE, NwConstants.DEL_FLOW);
195 updateRemoteAclFilterTable(portBefore, portBefore.getSecurityGroups(), deletedAaps, NwConstants.DEL_FLOW);
197 if (addedAaps != null && !addedAaps.isEmpty()) {
198 programAclWithAllowedAddress(portAfter, addedAaps, Action.UPDATE, NwConstants.ADD_FLOW);
199 updateRemoteAclFilterTable(portAfter, portAfter.getSecurityGroups(), addedAaps, NwConstants.ADD_FLOW);
201 if (portAfter.getSubnetInfo() != null && portBefore.getSubnetInfo() == null) {
202 programBroadcastRules(portAfter, NwConstants.ADD_FLOW);
204 handleSubnetChange(portBefore, portAfter);
206 List<Uuid> addedAcls = AclServiceUtils.getUpdatedAclList(portAfter.getSecurityGroups(),
207 portBefore.getSecurityGroups());
208 List<Uuid> deletedAcls = AclServiceUtils.getUpdatedAclList(portBefore.getSecurityGroups(),
209 portAfter.getSecurityGroups());
210 if (deletedAcls.isEmpty() && addedAcls.isEmpty()) {
211 LOG.trace("No change w.r.t ACL list for port={}", portAfter.getInterfaceId());
215 handleAclChange(portBefore, deletedAcls, NwConstants.DEL_FLOW);
216 handleAclChange(portAfter, addedAcls, NwConstants.ADD_FLOW);
219 private void handleSubnetChange(AclInterface portBefore, AclInterface portAfter) {
220 List<SubnetInfo> deletedSubnets =
221 AclServiceUtils.getSubnetDiff(portBefore.getSubnetInfo(), portAfter.getSubnetInfo());
222 List<SubnetInfo> addedSubnets =
223 AclServiceUtils.getSubnetDiff(portAfter.getSubnetInfo(), portBefore.getSubnetInfo());
225 if (deletedSubnets != null && !deletedSubnets.isEmpty()) {
226 programIcmpv6RARule(portAfter, deletedSubnets, NwConstants.DEL_FLOW);
228 if (addedSubnets != null && !addedSubnets.isEmpty()) {
229 programIcmpv6RARule(portAfter, addedSubnets, NwConstants.ADD_FLOW);
233 private void handleAclChange(AclInterface port, List<Uuid> aclList, int addOrRemove) {
234 int operationForAclRules = (addOrRemove == NwConstants.DEL_FLOW) ? NwConstants.MOD_FLOW : addOrRemove;
235 programAclRules(port, aclList, operationForAclRules);
236 updateRemoteAclFilterTable(port, aclList, port.getAllowedAddressPairs(), addOrRemove);
237 programAclDispatcherTable(port, addOrRemove);
240 protected SortedSet<Integer> getRemoteAclTags(AclInterface port) {
241 return this.direction == DirectionIngress.class ? port.getIngressRemoteAclTags()
242 : port.getEgressRemoteAclTags();
245 protected void programAclDispatcherTable(AclInterface port, int addOrRemove) {
246 SortedSet<Integer> remoteAclTags = getRemoteAclTags(port);
247 if (remoteAclTags.isEmpty()) {
248 LOG.debug("No {} rules with remote group id for port={}", this.directionString, port.getInterfaceId());
251 Integer firstRemoteAclTag = remoteAclTags.first();
252 Integer lastRemoteAclTag = remoteAclTags.last();
254 programFirstRemoteAclEntryInDispatcherTable(port, firstRemoteAclTag, addOrRemove);
255 programLastRemoteAclEntryInDispatcherTable(port, lastRemoteAclTag, addOrRemove);
257 Integer previousRemoteAclTag = firstRemoteAclTag;
258 for (Integer remoteAclTag : remoteAclTags) {
259 if (remoteAclTag.equals(firstRemoteAclTag)) {
262 List<MatchInfoBase> matches = new ArrayList<>();
263 matches.addAll(AclServiceUtils.buildMatchesForLPortTagAndRemoteAclTag(port.getLPortTag(),
264 previousRemoteAclTag, serviceMode));
265 String flowId = this.directionString + "_ACL_Dispatcher_" + port.getDpId() + "_" + port.getLPortTag() + "_"
268 List<InstructionInfo> instructions =
269 AclServiceOFFlowBuilder.getGotoInstructionInfo(getAclRuleBasedFilterTable());
270 instructions.add(AclServiceUtils.getWriteMetadataForRemoteAclTag(remoteAclTag));
271 syncFlow(port.getDpId(), getAclFilterCumDispatcherTable(), flowId,
272 AclConstants.ACE_GOTO_NEXT_REMOTE_ACL_PRIORITY, "ACL", 0, 0, AclConstants.COOKIE_ACL_BASE, matches,
273 instructions, addOrRemove);
275 previousRemoteAclTag = remoteAclTag;
279 protected void programFirstRemoteAclEntryInDispatcherTable(AclInterface port, Integer firstRemoteAclTag,
281 List<MatchInfoBase> matches = new ArrayList<>();
282 matches.add(AclServiceUtils.buildLPortTagMatch(port.getLPortTag(), serviceMode));
283 String flowId = this.directionString + "_ACL_Dispatcher_First_" + port.getDpId() + "_" + port.getLPortTag()
284 + "_" + firstRemoteAclTag;
286 List<InstructionInfo> instructions =
287 AclServiceOFFlowBuilder.getGotoInstructionInfo(getAclRuleBasedFilterTable());
288 instructions.add(AclServiceUtils.getWriteMetadataForRemoteAclTag(firstRemoteAclTag));
289 syncFlow(port.getDpId(), getAclFilterCumDispatcherTable(), flowId, AclConstants.ACE_FIRST_REMOTE_ACL_PRIORITY,
290 "ACL", 0, 0, AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
293 protected void programLastRemoteAclEntryInDispatcherTable(AclInterface port, Integer lastRemoteAclTag,
295 List<MatchInfoBase> matches = new ArrayList<>();
296 matches.addAll(AclServiceUtils.buildMatchesForLPortTagAndRemoteAclTag(port.getLPortTag(), lastRemoteAclTag,
298 String flowId = this.directionString + "_ACL_Dispatcher_Last_" + port.getDpId() + "_" + port.getLPortTag() + "_"
301 List<InstructionInfo> instructions = AclServiceOFFlowBuilder.getDropInstructionInfo();
302 syncFlow(port.getDpId(), getAclFilterCumDispatcherTable(), flowId, AclConstants.ACE_LAST_REMOTE_ACL_PRIORITY,
303 "ACL", 0, 0, AclServiceUtils.getDropFlowCookie(port.getLPortTag()), matches, instructions, addOrRemove);
306 private void programAcl(AclInterface port, Action action, int addOrRemove) {
307 programAclWithAllowedAddress(port, port.getAllowedAddressPairs(), action, addOrRemove);
310 private void programAclWithAllowedAddress(AclInterface port, List<AllowedAddressPairs> allowedAddresses,
311 Action action, int addOrRemove) {
312 BigInteger dpId = port.getDpId();
313 int lportTag = port.getLPortTag();
314 LOG.debug("Applying ACL Allowed Address on DpId {}, lportTag {}, Action {}", dpId, lportTag, action);
315 String portId = port.getInterfaceId();
316 programAntiSpoofingRules(port, allowedAddresses, action, addOrRemove);
317 programAclPortSpecificFixedRules(dpId, allowedAddresses, lportTag, portId, action, addOrRemove);
318 if (action == Action.ADD || action == Action.REMOVE) {
319 programAclRules(port, port.getSecurityGroups(), addOrRemove);
320 programAclDispatcherTable(port, addOrRemove);
325 * Programs the acl custom rules.
327 * @param port acl interface
328 * @param aclUuidList the list of acl uuid to be applied
329 * @param addOrRemove whether to delete or add flow
330 * @return program succeeded
332 protected boolean programAclRules(AclInterface port, List<Uuid> aclUuidList, int addOrRemove) {
333 BigInteger dpId = port.getDpId();
334 LOG.debug("Applying custom rules on DpId {}, lportTag {}", dpId, port.getLPortTag());
335 if (aclUuidList == null || dpId == null) {
336 LOG.warn("{} ACL parameters can not be null. dpId={}, aclUuidList={}", this.directionString, dpId,
340 for (Uuid aclUuid : aclUuidList) {
341 Acl acl = this.aclDataUtil.getAcl(aclUuid.getValue());
343 LOG.warn("The ACL {} not found in cache", aclUuid.getValue());
346 AccessListEntries accessListEntries = acl.getAccessListEntries();
347 List<Ace> aceList = accessListEntries.getAce();
348 for (Ace ace: aceList) {
349 programAceRule(port, aclUuid.getValue(), ace, addOrRemove);
356 * Programs the ace specific rule.
358 * @param port acl interface
359 * @param aclName the acl name
360 * @param ace rule to be program
361 * @param addOrRemove whether to delete or add flow
363 protected void programAceRule(AclInterface port, String aclName, Ace ace, int addOrRemove) {
364 SecurityRuleAttr aceAttr = AclServiceUtils.getAccesssListAttributes(ace);
365 if (!isValidDirection(aceAttr.getDirection())) {
366 LOG.trace("Ignoring {} direction while processing for {} ACE Rule {}", aceAttr.getDirection(),
367 this.directionString, ace.getRuleName());
370 LOG.debug("Program {} ACE rule for dpId={}, lportTag={}, addOrRemove={}, ace={}, portId={}",
371 this.directionString, port.getDpId(), port.getLPortTag(), addOrRemove, ace.getRuleName(),
372 port.getInterfaceId());
374 Matches matches = ace.getMatches();
375 Map<String, List<MatchInfoBase>> flowMap = null;
376 if (matches.getAceType() instanceof AceIp) {
377 flowMap = AclServiceOFFlowBuilder.programIpFlow(matches);
378 if (!AclServiceUtils.doesAceHaveRemoteGroupId(aceAttr)) {
379 // programming for ACE which doesn't have any remote group Id
380 programForAceNotHavingRemoteAclId(port, aclName, ace, flowMap, addOrRemove);
382 Uuid remoteAclId = aceAttr.getRemoteGroupId();
383 // programming for ACE which have remote group Id
384 programAceSpecificFlows(port, aclName, ace, flowMap, remoteAclId, addOrRemove);
389 protected void programForAceNotHavingRemoteAclId(AclInterface port, String aclName, Ace ace,
390 Map<String, List<MatchInfoBase>> flowMap, int addOrRemove) {
391 if (null == flowMap) {
394 MatchInfoBase lportTagMatch = AclServiceUtils.buildLPortTagMatch(port.getLPortTag(), serviceMode);
395 List<InstructionInfo> instructions = AclServiceOFFlowBuilder.getGotoInstructionInfo(getAclCommitterTable());
396 Integer flowPriority = this.aclServiceUtils.getAceFlowPriority(aclName);
398 for (Entry<String, List<MatchInfoBase>> entry : flowMap.entrySet()) {
399 String flowName = entry.getKey();
400 List<MatchInfoBase> matches = entry.getValue();
401 matches.add(lportTagMatch);
402 String flowId = flowName + this.directionString + "_" + port.getDpId() + "_" + port.getLPortTag() + "_"
403 + ace.key().getRuleName();
405 int operation = addOrRemove == NwConstants.MOD_FLOW ? NwConstants.DEL_FLOW : addOrRemove;
406 syncFlow(port.getDpId(), getAclFilterCumDispatcherTable(), flowId, flowPriority, "ACL", 0, 0,
407 AclConstants.COOKIE_ACL_BASE, matches, instructions, operation);
409 if (addOrRemove != NwConstants.DEL_FLOW) {
410 programAclForExistingTrafficTable(port, ace, addOrRemove, flowName, matches, flowPriority);
415 protected void programAceSpecificFlows(AclInterface port, String aclName, Ace ace,
416 Map<String, List<MatchInfoBase>> flowMap, Uuid remoteAclId, int addOrRemove) {
417 if (null == flowMap) {
420 Integer remoteAclTag = this.aclServiceUtils.getAclTag(remoteAclId);
421 if (remoteAclTag == null || remoteAclTag == AclConstants.INVALID_ACL_TAG) {
422 LOG.error("remoteAclTag={} is null or invalid for remoteAclId={}", remoteAclTag, remoteAclId);
425 List<MatchInfoBase> lportAndAclMatches =
426 AclServiceUtils.buildMatchesForLPortTagAndRemoteAclTag(port.getLPortTag(), remoteAclTag, serviceMode);
427 List<InstructionInfo> instructions = AclServiceOFFlowBuilder.getGotoInstructionInfo(getAclRemoteAclTable());
428 Integer flowPriority = this.aclServiceUtils.getAceFlowPriority(aclName);
430 for (Entry<String, List<MatchInfoBase>> entry : flowMap.entrySet()) {
431 String flowName = entry.getKey();
432 List<MatchInfoBase> matches = entry.getValue();
433 matches.addAll(lportAndAclMatches);
434 String flowId = flowName + this.directionString + "_" + port.getDpId() + "_" + port.getLPortTag() + "_"
435 + ace.key().getRuleName();
437 int operation = addOrRemove == NwConstants.MOD_FLOW ? NwConstants.DEL_FLOW : addOrRemove;
438 syncFlow(port.getDpId(), getAclRuleBasedFilterTable(), flowId, flowPriority, "ACL", 0, 0,
439 AclConstants.COOKIE_ACL_BASE, matches, instructions, operation);
441 if (addOrRemove != NwConstants.DEL_FLOW) {
442 programAclForExistingTrafficTable(port, ace, addOrRemove, flowName, matches,
448 private void programAclForExistingTrafficTable(AclInterface port, Ace ace, int addOrRemove, String flowName,
449 List<MatchInfoBase> matches, Integer priority) {
450 AceIp acl = (AceIp) ace.getMatches().getAceType();
451 final String newFlowName = flowName + this.directionString + "_" + port.getDpId() + "_" + port.getLPortTag()
452 + "_" + ((acl.getAceIpVersion() instanceof AceIpv4) ? "_IPv4" : "_IPv6") + "_FlowAfterRuleDeleted";
454 final List<MatchInfoBase> newMatches =
455 matches.stream().filter(obj -> !(obj instanceof NxMatchCtState || obj instanceof MatchMetadata))
456 .collect(Collectors.toList());
457 newMatches.add(AclServiceUtils.buildLPortTagMatch(port.getLPortTag(), serviceMode));
458 newMatches.add(new NxMatchCtState(AclConstants.TRACKED_RPL_CT_STATE, AclConstants.TRACKED_RPL_CT_STATE_MASK));
460 List<InstructionInfo> instructions =
461 AclServiceUtils.createCtMarkInstructionForNewState(getAclFilterCumDispatcherTable(), port.getElanId());
462 // Reversing the flow add/delete operation for this table.
463 int operation = (addOrRemove == NwConstants.ADD_FLOW) ? NwConstants.DEL_FLOW : NwConstants.ADD_FLOW;
464 syncFlow(port.getDpId(), getAclForExistingTrafficTable(), newFlowName, priority, "ACL", 0,
465 AclServiceUtils.getHardTimoutForApplyStatefulChangeOnExistingTraffic(ace, aclServiceUtils),
466 AclConstants.COOKIE_ACL_BASE, newMatches, instructions, operation);
470 public boolean removeAcl(AclInterface port) {
471 if (port.getDpId() == null) {
472 LOG.warn("Unable to find DP Id from ACL interface with id {}", port.getInterfaceId());
475 programAcl(port, Action.REMOVE, NwConstants.DEL_FLOW);
476 updateRemoteAclFilterTable(port, NwConstants.DEL_FLOW);
481 public boolean applyAce(AclInterface port, String aclName, Ace ace) {
482 if (!port.isPortSecurityEnabled() || port.getDpId() == null) {
485 programAceRule(port, aclName, ace, NwConstants.ADD_FLOW);
490 public boolean removeAce(AclInterface port, String aclName, Ace ace) {
491 if (!port.isPortSecurityEnabled() || port.getDpId() == null) {
494 programAceRule(port, aclName, ace, NwConstants.MOD_FLOW);
499 public void updateRemoteAcl(Acl aclBefore, Acl aclAfter, Collection<AclInterface> portsBefore) {
500 handleRemoteAclUpdate(aclBefore, aclAfter, portsBefore);
506 * @param aclInterface the acl interface
508 public abstract void bindService(AclInterface aclInterface);
513 * @param aclInterface the acl interface
515 protected abstract void unbindService(AclInterface aclInterface);
518 * Programs the anti-spoofing rules.
520 * @param port the acl interface
521 * @param allowedAddresses the allowed addresses
522 * @param action add/modify/remove action
523 * @param addOrRemove addorRemove
525 protected abstract void programAntiSpoofingRules(AclInterface port, List<AllowedAddressPairs> allowedAddresses,
526 Action action, int addOrRemove);
529 * Programs broadcast rules.
531 * @param port the Acl Interface port
532 * @param addOrRemove whether to delete or add flow
534 protected abstract void programBroadcastRules(AclInterface port, int addOrRemove);
536 protected abstract void programIcmpv6RARule(AclInterface port, List<SubnetInfo> subnets, int addOrRemove);
539 * Writes/remove the flow to/from the datastore.
558 * the list of matches to be writted
559 * @param instructions
560 * the list of instruction to be written.
562 * add or remove the entries.
564 protected void syncFlow(BigInteger dpId, short tableId, String flowId, int priority, String flowName,
565 int idleTimeOut, int hardTimeOut, BigInteger cookie, List<? extends MatchInfoBase> matches,
566 List<InstructionInfo> instructions, int addOrRemove) {
567 jobCoordinator.enqueueJob(flowName, () -> {
568 if (addOrRemove == NwConstants.DEL_FLOW) {
569 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, tableId, flowId, priority, flowName,
570 idleTimeOut, hardTimeOut, cookie, matches, null);
571 LOG.trace("Removing Acl Flow DpnId {}, flowId {}", dpId, flowId);
573 return Collections.singletonList(mdsalManager.removeFlow(dpId, flowEntity));
576 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, tableId, flowId, priority, flowName,
577 idleTimeOut, hardTimeOut, cookie, matches, instructions);
578 LOG.trace("Installing DpnId {}, flowId {}", dpId, flowId);
579 return Collections.singletonList(mdsalManager.installFlow(dpId, flowEntity));
584 protected List<InstructionInfo> getDispatcherTableResubmitInstructions() {
585 return getDispatcherTableResubmitInstructions(new ArrayList<>());
589 * Gets the dispatcher table resubmit instructions based on ingress/egress service mode w.r.t switch.
591 * @param actionsInfos
593 * @return the instructions for dispatcher table resubmit
595 protected List<InstructionInfo> getDispatcherTableResubmitInstructions(List<ActionInfo> actionsInfos) {
596 short dispatcherTableId = NwConstants.LPORT_DISPATCHER_TABLE;
597 if (ServiceModeEgress.class.equals(this.serviceMode)) {
598 dispatcherTableId = NwConstants.EGRESS_LPORT_DISPATCHER_TABLE;
601 List<InstructionInfo> instructions = new ArrayList<>();
602 actionsInfos.add(new ActionNxResubmit(dispatcherTableId));
603 instructions.add(new InstructionApplyActions(actionsInfos));
607 protected void handleRemoteAclUpdate(Acl aclBefore, Acl aclAfter, Collection<AclInterface> portsBefore) {
608 String aclName = aclAfter.getAclName();
609 Collection<AclInterface> interfaceList = aclDataUtil.getInterfaceList(new Uuid(aclName));
610 if (interfaceList == null || interfaceList.isEmpty()) {
611 LOG.trace("handleRemoteAclUpdate: No interfaces found with ACL={}", aclName);
614 Set<Uuid> remoteAclsBefore = AclServiceUtils.getRemoteAclIdsByDirection(aclBefore, this.direction);
615 Set<Uuid> remoteAclsAfter = AclServiceUtils.getRemoteAclIdsByDirection(aclAfter, this.direction);
617 Set<Uuid> remoteAclsAdded = new HashSet<>(remoteAclsAfter);
618 remoteAclsAdded.removeAll(remoteAclsBefore);
620 Set<Uuid> remoteAclsDeleted = new HashSet<>(remoteAclsBefore);
621 remoteAclsDeleted.removeAll(remoteAclsAfter);
623 if (!remoteAclsAdded.isEmpty() || !remoteAclsDeleted.isEmpty()) {
624 // delete and add flows in ACL dispatcher table for all applicable
626 for (AclInterface portBefore : portsBefore) {
627 programAclDispatcherTable(portBefore, NwConstants.DEL_FLOW);
629 for (AclInterface port : interfaceList) {
630 programAclDispatcherTable(port, NwConstants.ADD_FLOW);
633 Set<BigInteger> dpns = interfaceList.stream().map(AclInterface::getDpId).collect(Collectors.toSet());
635 programRemoteAclTable(aclName, remoteAclsDeleted, dpns, NwConstants.DEL_FLOW);
636 programRemoteAclTable(aclName, remoteAclsAdded, dpns, NwConstants.ADD_FLOW);
639 private void programRemoteAclTable(String aclName, Set<Uuid> remoteAclIds, Set<BigInteger> dpns, int addOrRemove) {
640 for (Uuid remoteAclId : remoteAclIds) {
641 Collection<AclInterface> remoteAclInterfaces = aclDataUtil.getInterfaceList(remoteAclId);
642 if (remoteAclInterfaces == null || remoteAclInterfaces.isEmpty()) {
645 Set<AllowedAddressPairs> aaps =
646 remoteAclInterfaces.stream().map(AclInterface::getAllowedAddressPairs).flatMap(List::stream)
647 .filter(AclServiceUtils::isNotIpAllNetwork).collect(Collectors.toSet());
649 Integer aclTag = aclServiceUtils.getAclTag(remoteAclId);
650 if (addOrRemove == NwConstants.ADD_FLOW) {
651 for (BigInteger dpn : dpns) {
652 for (AllowedAddressPairs aap : aaps) {
653 programRemoteAclTableFlow(dpn, aclTag, aap, addOrRemove);
656 } else if (addOrRemove == NwConstants.DEL_FLOW) {
657 Set<BigInteger> remoteAclDpns = new HashSet<>();
658 Map<String, Set<AclInterface>> mapAclWithPortSet =
659 aclDataUtil.getRemoteAclInterfaces(remoteAclId, this.direction);
660 if (mapAclWithPortSet != null) {
661 Map<String, Set<AclInterface>> copyOfMapAclWithPortSet = new HashMap<>(mapAclWithPortSet);
662 copyOfMapAclWithPortSet.remove(aclName);
663 remoteAclDpns = collectDpns(copyOfMapAclWithPortSet);
665 Set<BigInteger> dpnsToOperate = new HashSet<>(dpns);
666 dpnsToOperate.removeAll(remoteAclDpns);
668 "Deleting flows in Remote ACL table for remoteAclId={}, direction={}, dpnsToOperate={}, "
669 + "remoteAclDpns={}, dpns={}",
670 remoteAclId.getValue(), directionString, dpnsToOperate, remoteAclDpns, dpns);
672 for (BigInteger dpn : dpnsToOperate) {
673 for (AllowedAddressPairs aap : aaps) {
674 programRemoteAclTableFlow(dpn, aclTag, aap, addOrRemove);
681 private void updateRemoteAclFilterTable(AclInterface port, int addOrRemove) {
682 updateRemoteAclFilterTable(port, port.getSecurityGroups(), port.getAllowedAddressPairs(), addOrRemove);
685 private void updateRemoteAclFilterTable(AclInterface port, List<Uuid> aclList, List<AllowedAddressPairs> aaps,
687 if (aclList == null) {
688 LOG.debug("Port {} without SGs", port.getInterfaceId());
691 String portId = port.getInterfaceId();
692 LOG.trace("updateRemoteAclFilterTable for portId={}, aclList={}, aaps={}, addOrRemove={}", portId, aclList,
694 for (Uuid aclId : aclList) {
695 if (aclDataUtil.getRemoteAcl(aclId, this.direction) != null) {
696 Integer aclTag = aclServiceUtils.getAclTag(aclId);
697 if (addOrRemove == NwConstants.ADD_FLOW) {
698 syncRemoteAclTable(portId, aclId, aclTag, aaps, addOrRemove);
699 } else if (addOrRemove == NwConstants.DEL_FLOW) {
700 // Synchronizing during delete operation as there are
701 // look-ups for AclPortsLookup data.
702 synchronized (aclId.getValue().intern()) {
703 syncRemoteAclTable(portId, aclId, aclTag, aaps, addOrRemove);
708 Set<Uuid> remoteAclIds = aclServiceUtils.getRemoteAclIdsByDirection(aclList, direction);
709 for (Uuid remoteAclId : remoteAclIds) {
710 syncRemoteAclTableFromOtherDpns(port, remoteAclId, addOrRemove);
714 private void syncRemoteAclTable(String portId, Uuid acl, Integer aclTag, List<AllowedAddressPairs> aaps,
716 Map<String, Set<AclInterface>> mapAclWithPortSet = aclDataUtil.getRemoteAclInterfaces(acl, this.direction);
717 Set<BigInteger> dpns = collectDpns(mapAclWithPortSet);
718 for (AllowedAddressPairs aap : aaps) {
719 if (!AclServiceUtils.isNotIpAllNetwork(aap)) {
722 if (aclServiceUtils.skipDeleteInCaseOfOverlappingIP(portId, acl, aap.getIpAddress(),
724 LOG.debug("Skipping delete of IP={} in remote ACL table for remoteAclId={}, portId={}",
725 aap.getIpAddress(), portId, acl.getValue());
728 for (BigInteger dpId : dpns) {
729 programRemoteAclTableFlow(dpId, aclTag, aap, addOrRemove);
734 private void syncRemoteAclTableFromOtherDpns(AclInterface port, Uuid remoteAclId, int addOrRemove) {
735 Collection<AclInterface> aclInterfaces = aclDataUtil.getInterfaceList(remoteAclId);
737 if (aclInterfaces != null && !aclInterfaces.isEmpty() && isFirstPortInDpnWithRemoteAclId(port, remoteAclId)) {
738 Integer aclTag = aclServiceUtils.getAclTag(remoteAclId);
739 for (AclInterface aclInterface : aclInterfaces) {
740 if (port.getInterfaceId().equals(aclInterface.getInterfaceId())) {
743 for (AllowedAddressPairs aap : aclInterface.getAllowedAddressPairs()) {
744 if (AclServiceUtils.isNotIpAllNetwork(aap)) {
745 programRemoteAclTableFlow(port.getDpId(), aclTag, aap, addOrRemove);
752 private boolean isFirstPortInDpnWithRemoteAclId(AclInterface port, Uuid remoteAclId) {
753 String portId = port.getInterfaceId();
754 BigInteger dpId = port.getDpId();
755 Map<String, Set<AclInterface>> remoteAclInterfacesMap =
756 aclDataUtil.getRemoteAclInterfaces(remoteAclId, direction);
757 if (remoteAclInterfacesMap != null) {
758 for (Set<AclInterface> interfaceSet : remoteAclInterfacesMap.values()) {
759 for (AclInterface aclInterface : interfaceSet) {
760 if (portId.equals(aclInterface.getInterfaceId())) {
763 if (dpId.equals(aclInterface.getDpId())) {
772 protected abstract void programRemoteAclTableFlow(BigInteger dpId, Integer aclTag, AllowedAddressPairs aap,
775 protected String getOperAsString(int flowOper) {
778 case NwConstants.ADD_FLOW:
781 case NwConstants.DEL_FLOW:
784 case NwConstants.MOD_FLOW:
793 protected Set<BigInteger> collectDpns(Map<String, Set<AclInterface>> mapAclWithPortSet) {
794 Set<BigInteger> dpns = new HashSet<>();
795 if (mapAclWithPortSet == null) {
798 for (Set<AclInterface> innerSet : mapAclWithPortSet.values()) {
799 if (innerSet == null) {
802 for (AclInterface inter : innerSet) {
803 dpns.add(inter.getDpId());
810 * Programs the port specific fixed rules.
812 * @param dpId the dp id
813 * @param allowedAddresses the allowed addresses
814 * @param lportTag the lport tag
815 * @param portId the portId
816 * @param action the action
817 * @param write whether to add or remove the flow.
819 protected void programAclPortSpecificFixedRules(BigInteger dpId, List<AllowedAddressPairs> allowedAddresses,
820 int lportTag, String portId, Action action, int write) {
821 programGotoClassifierTableRules(dpId, allowedAddresses, lportTag, write);
822 if (action == Action.ADD || action == Action.REMOVE) {
823 programConntrackRecircRules(dpId, allowedAddresses, lportTag, portId, write);
824 programPortSpecificDropRules(dpId, lportTag, write);
825 programAclCommitRules(dpId, lportTag, portId, write);
827 LOG.info("programAclPortSpecificFixedRules: flows for dpId={}, lportId={}, action={}, write={}", dpId, lportTag,
831 protected abstract void programGotoClassifierTableRules(BigInteger dpId, List<AllowedAddressPairs> aaps,
832 int lportTag, int addOrRemove);
835 * Adds the rule to send the packet to the netfilter to check whether it is a known packet.
837 * @param dpId the dpId
838 * @param aaps the allowed address pairs
839 * @param lportTag the lport tag
840 * @param portId the portId
841 * @param addOrRemove whether to add or remove the flow
843 protected void programConntrackRecircRules(BigInteger dpId, List<AllowedAddressPairs> aaps, int lportTag,
844 String portId, int addOrRemove) {
845 if (AclServiceUtils.doesIpv4AddressExists(aaps)) {
846 programConntrackRecircRule(dpId, lportTag, portId, MatchEthernetType.IPV4, addOrRemove);
848 if (AclServiceUtils.doesIpv6AddressExists(aaps)) {
849 programConntrackRecircRule(dpId, lportTag, portId, MatchEthernetType.IPV6, addOrRemove);
853 protected void programConntrackRecircRule(BigInteger dpId, int lportTag, String portId,
854 MatchEthernetType matchEtherType, int addOrRemove) {
855 List<MatchInfoBase> matches = new ArrayList<>();
856 matches.add(matchEtherType);
857 matches.add(AclServiceUtils.buildLPortTagMatch(lportTag, serviceMode));
859 List<InstructionInfo> instructions = new ArrayList<>();
860 if (addOrRemove == NwConstants.ADD_FLOW) {
861 Long elanTag = getElanIdFromAclInterface(portId);
862 if (elanTag == null) {
863 LOG.error("ElanId not found for portId={}; Context: dpId={}, lportTag={}, addOrRemove={},", portId,
864 dpId, lportTag, addOrRemove);
867 List<ActionInfo> actionsInfos = new ArrayList<>();
868 actionsInfos.add(new ActionNxConntrack(2, 0, 0, elanTag.intValue(), getAclForExistingTrafficTable()));
869 instructions.add(new InstructionApplyActions(actionsInfos));
873 this.directionString + "_Fixed_Conntrk_" + dpId + "_" + lportTag + "_" + matchEtherType + "_Recirc";
874 syncFlow(dpId, getAclConntrackSenderTable(), flowName, AclConstants.ACL_DEFAULT_PRIORITY, "ACL", 0, 0,
875 AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
879 * Adds the rules to drop the unknown/invalid packets .
881 * @param dpId the dpId
882 * @param lportTag the lport tag
883 * @param addOrRemove whether to add or remove the flow
885 protected void programPortSpecificDropRules(BigInteger dpId, int lportTag, int addOrRemove) {
886 LOG.debug("Programming Drop Rules: DpId={}, lportTag={}, addOrRemove={}", dpId, lportTag, addOrRemove);
887 programConntrackInvalidDropRule(dpId, lportTag, addOrRemove);
888 programAclRuleMissDropRule(dpId, lportTag, addOrRemove);
892 * Adds the rule to drop the conntrack invalid packets .
894 * @param dpId the dpId
895 * @param lportTag the lport tag
896 * @param addOrRemove whether to add or remove the flow
898 protected void programConntrackInvalidDropRule(BigInteger dpId, int lportTag, int addOrRemove) {
899 List<MatchInfoBase> matches = AclServiceOFFlowBuilder.addLPortTagMatches(lportTag,
900 AclConstants.TRACKED_INV_CT_STATE, AclConstants.TRACKED_INV_CT_STATE_MASK, serviceMode);
901 List<InstructionInfo> instructions = AclServiceOFFlowBuilder.getDropInstructionInfo();
903 String flowId = this.directionString + "_Fixed_Conntrk_Drop" + dpId + "_" + lportTag + "_Tracked_Invalid";
904 syncFlow(dpId, getAclFilterCumDispatcherTable(), flowId, AclConstants.CT_STATE_TRACKED_INVALID_PRIORITY, "ACL",
905 0, 0, AclServiceUtils.getDropFlowCookie(lportTag), matches, instructions, addOrRemove);
909 * Program ACL rule miss drop rule for a port.
911 * @param dpId the dp id
912 * @param lportTag the lport tag
913 * @param addOrRemove the add or remove
915 protected void programAclRuleMissDropRule(BigInteger dpId, int lportTag, int addOrRemove) {
916 List<MatchInfoBase> matches = new ArrayList<>();
917 matches.add(AclServiceUtils.buildLPortTagMatch(lportTag, serviceMode));
918 List<InstructionInfo> instructions = AclServiceOFFlowBuilder.getDropInstructionInfo();
920 String flowId = this.directionString + "_Fixed_Acl_Rule_Miss_Drop_" + dpId + "_" + lportTag;
921 syncFlow(dpId, getAclFilterCumDispatcherTable(), flowId, AclConstants.ACL_PORT_SPECIFIC_DROP_PRIORITY, "ACL", 0,
922 0, AclServiceUtils.getDropFlowCookie(lportTag), matches, instructions, addOrRemove);
926 * Program acl commit rules.
928 * @param dpId the dp id
929 * @param lportTag the lport tag
930 * @param portId the port id
931 * @param addOrRemove the add or remove
933 protected void programAclCommitRules(BigInteger dpId, int lportTag, String portId, int addOrRemove) {
934 programAclCommitRuleForConntrack(dpId, lportTag, portId, MatchEthernetType.IPV4, addOrRemove);
935 programAclCommitRuleForConntrack(dpId, lportTag, portId, MatchEthernetType.IPV6, addOrRemove);
936 programAclCommitRuleForNonConntrack(dpId, lportTag, addOrRemove);
940 * Program acl commit rule for conntrack.
942 * @param dpId the dp id
943 * @param lportTag the lport tag
944 * @param portId the port id
945 * @param matchEtherType the match ether type
946 * @param addOrRemove the add or remove
948 protected void programAclCommitRuleForConntrack(BigInteger dpId, int lportTag, String portId,
949 MatchEthernetType matchEtherType, int addOrRemove) {
950 List<MatchInfoBase> matches = new ArrayList<>();
951 matches.add(matchEtherType);
952 matches.addAll(AclServiceUtils.buildMatchesForLPortTagAndConntrackClassifierType(lportTag,
953 AclConntrackClassifierType.CONNTRACK_SUPPORTED, serviceMode));
955 List<ActionInfo> actionsInfos = new ArrayList<>();
956 if (addOrRemove == NwConstants.ADD_FLOW) {
957 Long elanId = getElanIdFromAclInterface(portId);
958 if (elanId == null) {
959 LOG.error("ElanId not found for portId={}; Context: dpId={}, lportTag={}, addOrRemove={}", portId, dpId,
960 lportTag, addOrRemove);
963 List<NxCtAction> ctActionsList =
964 Lists.newArrayList(new ActionNxConntrack.NxCtMark(AclConstants.CT_MARK_EST_STATE));
965 actionsInfos.add(new ActionNxConntrack(2, 1, 0, elanId.intValue(), (short) 255, ctActionsList));
967 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions(actionsInfos);
969 String flowName = directionString + "_Acl_Commit_Conntrack_" + dpId + "_" + lportTag + "_" + matchEtherType;
970 // Flow for conntrack traffic to commit and resubmit to dispatcher
971 syncFlow(dpId, getAclCommitterTable(), flowName, AclConstants.ACL_DEFAULT_PRIORITY, "ACL", 0, 0,
972 AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
976 * Program acl commit rule for non conntrack.
978 * @param dpId the dp id
979 * @param lportTag the lport tag
980 * @param addOrRemove the add or remove
982 protected void programAclCommitRuleForNonConntrack(BigInteger dpId, int lportTag, int addOrRemove) {
983 List<MatchInfoBase> matches = new ArrayList<>();
984 matches.addAll(AclServiceUtils.buildMatchesForLPortTagAndConntrackClassifierType(lportTag,
985 AclConntrackClassifierType.NON_CONNTRACK_SUPPORTED, serviceMode));
987 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
988 String flowName = this.directionString + "_Acl_Commit_Non_Conntrack_" + dpId + "_" + lportTag;
989 // Flow for non-conntrack traffic to resubmit to dispatcher
990 syncFlow(dpId, getAclCommitterTable(), flowName, AclConstants.ACL_DEFAULT_PRIORITY, "ACL", 0, 0,
991 AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
994 protected Long getElanIdFromAclInterface(String elanInterfaceName) {
995 AclInterface aclInterface = aclInterfaceCache.get(elanInterfaceName);
996 if (null != aclInterface) {
997 return aclInterface.getElanId();
1002 protected abstract boolean isValidDirection(Class<? extends DirectionBase> direction);
1004 protected abstract short getAclAntiSpoofingTable();
1006 protected abstract short getAclConntrackClassifierTable();
1008 protected abstract short getAclConntrackSenderTable();
1010 protected abstract short getAclForExistingTrafficTable();
1012 protected abstract short getAclFilterCumDispatcherTable();
1014 protected abstract short getAclRuleBasedFilterTable();
1016 protected abstract short getAclRemoteAclTable();
1018 protected abstract short getAclCommitterTable();