2 * Copyright (c) 2016 Ericsson India Global Services Pvt Ltd. 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.listeners;
10 import java.util.ArrayList;
11 import java.util.List;
12 import javax.annotation.PreDestroy;
13 import javax.inject.Inject;
14 import javax.inject.Singleton;
15 import org.eclipse.jdt.annotation.Nullable;
16 import org.opendaylight.infrautils.utils.concurrent.Executors;
17 import org.opendaylight.mdsal.binding.api.ClusteredDataTreeChangeListener;
18 import org.opendaylight.mdsal.binding.api.DataBroker;
19 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
20 import org.opendaylight.netvirt.aclservice.api.AclInterfaceCache;
21 import org.opendaylight.netvirt.aclservice.api.AclServiceManager;
22 import org.opendaylight.netvirt.aclservice.api.AclServiceManager.Action;
23 import org.opendaylight.netvirt.aclservice.api.utils.AclInterface;
24 import org.opendaylight.netvirt.aclservice.utils.AclClusterUtil;
25 import org.opendaylight.netvirt.aclservice.utils.AclDataUtil;
26 import org.opendaylight.netvirt.aclservice.utils.AclServiceUtils;
27 import org.opendaylight.serviceutils.srm.RecoverableListener;
28 import org.opendaylight.serviceutils.srm.ServiceRecoveryRegistry;
29 import org.opendaylight.serviceutils.tools.listener.AbstractAsyncDataTreeChangeListener;
30 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
31 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
32 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.ParentRefs;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.DirectionEgress;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.DirectionIngress;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.InterfaceAcl;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.interfaces._interface.AllowedAddressPairs;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.interfaces._interface.SubnetInfo;
39 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
44 public class AclInterfaceListener extends AbstractAsyncDataTreeChangeListener<Interface>
45 implements ClusteredDataTreeChangeListener<Interface>, RecoverableListener {
46 private static final Logger LOG = LoggerFactory.getLogger(AclInterfaceListener.class);
48 private final AclServiceManager aclServiceManager;
49 private final AclClusterUtil aclClusterUtil;
50 private final DataBroker dataBroker;
51 private final AclDataUtil aclDataUtil;
52 private final AclInterfaceCache aclInterfaceCache;
53 private final AclServiceUtils aclServiceUtils;
56 public AclInterfaceListener(AclServiceManager aclServiceManager, AclClusterUtil aclClusterUtil,
57 DataBroker dataBroker, AclDataUtil aclDataUtil, AclInterfaceCache aclInterfaceCache,
58 AclServiceUtils aclServicUtils, ServiceRecoveryRegistry serviceRecoveryRegistry) {
59 super(dataBroker, LogicalDatastoreType.CONFIGURATION,
60 InstanceIdentifier.create(Interfaces.class).child(Interface.class),
61 Executors.newListeningSingleThreadExecutor("AclEventListener", LOG));
62 this.aclServiceManager = aclServiceManager;
63 this.aclClusterUtil = aclClusterUtil;
64 this.dataBroker = dataBroker;
65 this.aclDataUtil = aclDataUtil;
66 this.aclInterfaceCache = aclInterfaceCache;
67 this.aclServiceUtils = aclServicUtils;
68 serviceRecoveryRegistry.addRecoverableListener(AclServiceUtils.getRecoverServiceRegistryKey(), this);
72 LOG.info("{} start", getClass().getSimpleName());
76 public void registerListener() {
81 public void deregisterListener() {
86 public void remove(InstanceIdentifier<Interface> key, Interface port) {
87 LOG.trace("Received AclInterface remove event, port={}", port);
88 String interfaceId = port.getName();
89 AclInterface aclInterface = aclInterfaceCache.remove(interfaceId);
90 if (AclServiceUtils.isOfInterest(aclInterface)) {
91 if (aclClusterUtil.isEntityOwner()) {
92 LOG.debug("On remove event, notify ACL unbind/remove for interface: {}", interfaceId);
93 aclServiceManager.notify(aclInterface, null, Action.UNBIND);
94 if (aclInterface.getDpId() != null) {
95 aclServiceManager.notify(aclInterface, null, Action.REMOVE);
102 public void update(@Nullable InstanceIdentifier<Interface> key, Interface portBefore, Interface portAfter) {
103 if (portBefore.augmentation(ParentRefs.class) == null
104 && portAfter.augmentation(ParentRefs.class) != null) {
105 LOG.trace("Ignoring event for update in ParentRefs for {} ", portAfter.getName());
108 LOG.trace("Received AclInterface update event, portBefore={}, portAfter={}", portBefore, portAfter);
109 InterfaceAcl aclInPortAfter = portAfter.augmentation(InterfaceAcl.class);
110 InterfaceAcl aclInPortBefore = portBefore.augmentation(InterfaceAcl.class);
112 String interfaceId = portAfter.getName();
113 org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state
114 .Interface interfaceState = AclServiceUtils.getInterfaceStateFromOperDS(dataBroker, interfaceId);
116 AclInterface aclInterfaceBefore = aclInterfaceCache.get(interfaceId);
117 if (aclInterfaceBefore == null || isPortSecurityEnabledNow(aclInPortBefore, aclInPortAfter)) {
118 // Updating cache now as it might have not updated when
119 // port-security-enable=false
120 aclInterfaceBefore = addOrUpdateAclInterfaceCache(interfaceId, aclInPortBefore, true, interfaceState);
122 if (AclServiceUtils.isOfInterest(aclInPortAfter) || AclServiceUtils.isOfInterest(aclInPortBefore)) {
123 List<Uuid> sgsBefore = null;
124 if (aclInPortBefore != null) {
125 sgsBefore = aclInPortBefore.getSecurityGroups();
127 boolean isSgChanged = isSecurityGroupsChanged(sgsBefore, aclInPortAfter.getSecurityGroups());
128 AclInterface aclInterfaceAfter =
129 addOrUpdateAclInterfaceCache(interfaceId, aclInPortAfter, isSgChanged, interfaceState);
130 updateCacheWithAddedAcls(aclInterfaceBefore, aclInterfaceAfter);
132 if (aclClusterUtil.isEntityOwner()) {
133 // Handle bind/unbind service irrespective of interface state (up/down)
134 boolean isPortSecurityEnable = aclInterfaceAfter.isPortSecurityEnabled();
135 boolean isPortSecurityEnableBefore = aclInterfaceBefore.isPortSecurityEnabled();
136 // if port security enable is changed and is disabled, unbind ACL service
137 if (isPortSecurityEnableBefore != isPortSecurityEnable && !isPortSecurityEnable) {
138 LOG.debug("Notify unbind ACL service for interface={}, isPortSecurityEnable={}", interfaceId,
139 isPortSecurityEnable);
140 aclServiceManager.notify(aclInterfaceAfter, null, Action.UNBIND);
142 if (interfaceState != null && org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces
143 .rev140508.interfaces.state.Interface.OperStatus.Up.equals(interfaceState.getOperStatus())) {
144 // if port security enable is changed and is enabled, bind ACL service
145 if (isPortSecurityEnableBefore != isPortSecurityEnable && isPortSecurityEnable) {
146 LOG.debug("Notify bind ACL service for interface={}, isPortSecurityEnable={}", interfaceId,
147 isPortSecurityEnable);
148 aclServiceManager.notify(aclInterfaceAfter, null, Action.BIND);
150 LOG.debug("On update event, notify ACL service manager to update ACL for interface: {}",
152 // handle add for AclPortsLookup before processing update
153 aclServiceUtils.addAclPortsLookupForInterfaceUpdate(aclInterfaceBefore, aclInterfaceAfter);
155 aclServiceManager.notify(aclInterfaceAfter, aclInterfaceBefore, AclServiceManager.Action.UPDATE);
156 // handle delete for AclPortsLookup after processing update
157 aclServiceUtils.deleteAclPortsLookupForInterfaceUpdate(aclInterfaceBefore, aclInterfaceAfter);
160 updateCacheWithAclChange(aclInterfaceBefore, aclInterfaceAfter);
164 private void updateCacheWithAclChange(AclInterface aclInterfaceBefore, AclInterface aclInterfaceAfter) {
165 List<Uuid> deletedAcls = AclServiceUtils.getUpdatedAclList(aclInterfaceBefore.getSecurityGroups(),
166 aclInterfaceAfter.getSecurityGroups());
167 if (!deletedAcls.isEmpty()) {
168 aclDataUtil.removeAclInterfaceMap(deletedAcls, aclInterfaceAfter);
170 List<AllowedAddressPairs> addedAap = AclServiceUtils.getUpdatedAllowedAddressPairs(aclInterfaceAfter
171 .getAllowedAddressPairs(), aclInterfaceBefore.getAllowedAddressPairs());
172 List<AllowedAddressPairs> deletedAap = AclServiceUtils.getUpdatedAllowedAddressPairs(aclInterfaceBefore
173 .getAllowedAddressPairs(), aclInterfaceAfter.getAllowedAddressPairs());
174 if (!deletedAap.isEmpty() || !addedAap.isEmpty()) {
175 LOG.debug("Update cache with new AAP = {}", aclInterfaceAfter.getInterfaceId());
176 aclDataUtil.addOrUpdateAclInterfaceMap(aclInterfaceAfter.getSecurityGroups(), aclInterfaceAfter);
180 private void updateCacheWithAddedAcls(AclInterface aclInterfaceBefore, AclInterface aclInterfaceAfter) {
181 List<Uuid> addedAcls = AclServiceUtils.getUpdatedAclList(aclInterfaceAfter.getSecurityGroups(),
182 aclInterfaceBefore.getSecurityGroups());
183 if (addedAcls != null && !addedAcls.isEmpty()) {
184 LOG.debug("Update cache by adding interface={}", aclInterfaceAfter.getInterfaceId());
185 aclDataUtil.addOrUpdateAclInterfaceMap(addedAcls, aclInterfaceAfter);
189 private boolean isPortSecurityEnabledNow(InterfaceAcl aclInPortBefore, InterfaceAcl aclInPortAfter) {
190 return aclInPortBefore != null && !aclInPortBefore.isPortSecurityEnabled() && aclInPortAfter != null
191 && aclInPortAfter.isPortSecurityEnabled();
194 private boolean isSecurityGroupsChanged(@Nullable List<Uuid> sgsBefore, @Nullable List<Uuid> sgsAfter) {
195 if (sgsBefore == null && sgsAfter == null) {
198 if (sgsBefore == null || sgsAfter == null) {
201 return !(sgsBefore.containsAll(sgsAfter) && sgsAfter.containsAll(sgsBefore));
204 private AclInterface addOrUpdateAclInterfaceCache(String interfaceId, InterfaceAcl aclInPort) {
205 return addOrUpdateAclInterfaceCache(interfaceId, aclInPort, true, null);
208 private AclInterface addOrUpdateAclInterfaceCache(String interfaceId, InterfaceAcl aclInPort, boolean isSgChanged,
209 org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state
210 .@Nullable Interface interfaceState) {
211 AclInterface aclInterface = aclInterfaceCache.addOrUpdate(interfaceId, (prevAclInterface, builder) -> {
212 List<Uuid> sgs = new ArrayList<>();
213 if (aclInPort != null) {
214 sgs = aclInPort.getSecurityGroups();
215 builder.portSecurityEnabled(aclInPort.isPortSecurityEnabled())
216 .interfaceType(aclInPort.getInterfaceType()).securityGroups(sgs)
217 .allowedAddressPairs(new ArrayList<AllowedAddressPairs>(aclInPort
218 .getAllowedAddressPairs().values())).subnetInfo(new ArrayList<SubnetInfo>(aclInPort
219 .getSubnetInfo().values()));
222 if ((prevAclInterface == null || prevAclInterface.getLPortTag() == null) && interfaceState != null) {
223 builder.dpId(AclServiceUtils.getDpIdFromIterfaceState(interfaceState))
224 .lPortTag(interfaceState.getIfIndex()).isMarkedForDelete(false);
227 if (prevAclInterface == null || prevAclInterface.getElanId() == null) {
228 builder.elanId(AclServiceUtils.getElanIdFromInterface(interfaceId, dataBroker));
230 if (prevAclInterface == null || isSgChanged) {
231 builder.ingressRemoteAclTags(aclServiceUtils.getRemoteAclTags(sgs, DirectionIngress.class))
232 .egressRemoteAclTags(aclServiceUtils.getRemoteAclTags(sgs, DirectionEgress.class));
235 // Clone and return the ACL interface object
236 return AclInterface.builder(aclInterface).build();
240 public void add(InstanceIdentifier<Interface> key, Interface port) {
241 LOG.trace("Received AclInterface add event, port={}", port);
242 InterfaceAcl aclInPort = port.augmentation(InterfaceAcl.class);
243 if (AclServiceUtils.isOfInterest(aclInPort)) {
244 String interfaceId = port.getName();
245 AclInterface aclInterface = addOrUpdateAclInterfaceCache(interfaceId, aclInPort);
247 // if interface state event comes first followed by interface config event.
248 if (aclInterface.getDpId() != null && aclInterface.getElanId() != null && aclClusterUtil.isEntityOwner()) {
249 LOG.debug("On add event, notify ACL bind/add for interface: {}", interfaceId);
250 aclServiceManager.notify(aclInterface, null, Action.BIND);
251 aclServiceManager.notify(aclInterface, null, Action.ADD);
258 public void close() {
260 Executors.shutdownAndAwaitTermination(getExecutorService());