/* * Copyright (c) 2017 Cisco Systems, Inc. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.groupbasedpolicy.renderer.vpp.policy.acl; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.groupbasedpolicy.renderer.util.AddressEndpointUtils; import org.opendaylight.groupbasedpolicy.renderer.vpp.iface.InterfaceManager; import org.opendaylight.groupbasedpolicy.renderer.vpp.iface.VppPathMapper; import org.opendaylight.groupbasedpolicy.renderer.vpp.policy.PolicyContext; import org.opendaylight.groupbasedpolicy.renderer.vpp.policy.acl.AccessListUtil.ACE_DIRECTION; import org.opendaylight.groupbasedpolicy.renderer.vpp.util.KeyFactory; import org.opendaylight.groupbasedpolicy.renderer.vpp.util.MountedDataBrokerProvider; import org.opendaylight.groupbasedpolicy.util.EndpointUtils; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.endpoints.address.endpoints.AddressEndpointKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.has.absolute.location.absolute.location.location.type.ExternalLocationCase; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.l2_l3.rev160427.L2BridgeDomain; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.endpoints.AddressEndpointWithLocation; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.renderer.endpoints.RendererEndpointKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.renderer.endpoints.renderer.endpoint.PeerEndpointKey; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableTable; import com.google.common.collect.ImmutableTable.Builder; import com.google.common.collect.Sets; public class AclManager { private static final Logger LOG = LoggerFactory.getLogger(AclManager.class); private final MountedDataBrokerProvider mountDataProvider; private static ImmutableTable<NodeId, InterfaceKey, ImmutableSet<AddressEndpointKey>> multipleEndpointsOnInterface; public AclManager(@Nonnull MountedDataBrokerProvider mountDataProvider) { this.mountDataProvider = Preconditions.checkNotNull(mountDataProvider); } public List<AccessListWrapper> resolveAclsOnInterface(RendererEndpointKey rEpKey, PolicyContext ctx) { List<AccessListWrapper> aclWrappers = new ArrayList<>(); for (ACE_DIRECTION dir : new ACE_DIRECTION[] {ACE_DIRECTION.INGRESS, ACE_DIRECTION.EGRESS}) { aclWrappers.add(buildAccessListWrappers(dir, ctx, rEpKey)); } return aclWrappers; } /** * @param policyDirection direction for which policy should be resolved. EP -> VPP = OUTBOUND, EP <- VPP = INBOUND * @param ctx with cached data * @param rEpKey key of EP for which to create ACLs. * @return synchronization futures, so that INGRESS and EGRESS ACLS can be resolved in parallel. */ private static AccessListWrapper buildAccessListWrappers(ACE_DIRECTION policyDirection, PolicyContext ctx, RendererEndpointKey rEpKey) { LOG.trace("Resolving policy for VPP renderer endpoint {} in a separate thread in {} direction.", rEpKey, policyDirection); AccessListWrapper aclWrapper = AccessListUtil.ACE_DIRECTION.INGRESS .equals(policyDirection) ? new IngressAccessListWrapper() : new EgressAccessListWrapper(); AccessListUtil.configureLocalRules(ctx, rEpKey, policyDirection, aclWrapper); // we support multiple IP end-points on a same interface for (AddressEndpointKey aek : otherEndpointsOnTheSameInterface(ctx, AddressEndpointUtils.fromRendererEpKey(rEpKey))) { AccessListUtil.configureLocalRules(ctx, AddressEndpointUtils.toRendererEpKey(aek), policyDirection, aclWrapper); } // resolve peers with no location aclWrapper.writeRules(AccessListUtil.denyDomainSubnets(ctx, policyDirection)); // TODO currently any traffic heading to/from outside of managed domain is // permitted for demonstration purposes if (rEpKey.getContextType().isAssignableFrom(L2BridgeDomain.class) && AccessListUtil.findAddrEp(ctx, rEpKey) != null) { Optional<GbpAceBuilder> allowExtAccess = AccessListUtil.allowExternalNetworksForEp(AccessListUtil.findAddrEp(ctx, rEpKey), policyDirection); if (allowExtAccess.isPresent()) { aclWrapper.writeRule(allowExtAccess.get()); } } return aclWrapper; } public void updateAclsForPeers(PolicyContext policyCtx, RendererEndpointKey rEpKey) { ImmutableSet<PeerEndpointKey> peers = policyCtx.getPolicyTable().row(rEpKey).keySet(); for (RendererEndpointKey peerRendEp : peers.stream() .map(AddressEndpointUtils::fromPeerEpKey) .collect(Collectors.toList()) .stream() .map(AddressEndpointUtils::toRendererEpKey) .collect(Collectors.toList())) { updateAclsForRendEp(peerRendEp, policyCtx); } } public void updateAclsForRendEp(RendererEndpointKey rEpKey, PolicyContext policyCtx) { LOG.info("Updating policy for endpoint {}", rEpKey); AddressEndpointWithLocation peerAddrEp = policyCtx.getAddrEpByKey().get(KeyFactory.addressEndpointKey(rEpKey)); ExternalLocationCase epLoc; try { epLoc = InterfaceManager.resolveAndValidateLocation(peerAddrEp); } catch (NullPointerException | IllegalArgumentException e) { //TODO investigate, don't just move on. LOG.warn("Peer {} has no location. Moving on...", peerAddrEp, e.getMessage()); return; } InstanceIdentifier<?> vppNodeIid = epLoc.getExternalNodeMountPoint(); Optional<InstanceIdentifier<Interface>> optInterfaceIid = VppPathMapper.interfaceToInstanceIdentifier(epLoc.getExternalNodeConnector()); if (!optInterfaceIid.isPresent()) { LOG.warn("Cannot find interface for endpoint {}. ACLs for endpoint not updated {}. ", rEpKey); return; } Optional<DataBroker> optMountPoint = mountDataProvider.getDataBrokerForMountPoint(vppNodeIid); resolveAclsOnInterface(rEpKey, policyCtx).forEach(aclWrapper -> aclWrapper .writeAcl(optMountPoint.get(), optInterfaceIid.get().firstKeyOf(Interface.class))); } /** * Cache end-points accessible via a single interface for further processing. */ public void cacheMultiInterfaces(@Nonnull PolicyContext ctx) { Builder<NodeId, InterfaceKey, ImmutableSet<AddressEndpointKey>> resultBuilder = new Builder<>(); resolveEndpointsOnMultipleInterface(ImmutableList.copyOf(ctx.getAddrEpByKey().values()), resultBuilder); multipleEndpointsOnInterface = resultBuilder.build(); } private void resolveEndpointsOnMultipleInterface(@Nullable ImmutableList<AddressEndpointWithLocation> eps, @Nonnull Builder<NodeId, InterfaceKey, ImmutableSet<AddressEndpointKey>> builder) { if (eps == null || eps.isEmpty()) { return; } eps.get(0); ImmutableSet<AddressEndpointKey> copyOf = ImmutableSet.copyOf(eps.stream() .filter(addrEp -> AddressEndpointUtils.sameExternalLocationCase(eps.get(0), addrEp)) .map(addrEp -> AddressEndpointUtils.fromAddressEndpointWithLocationKey(addrEp.getKey())) .collect(Collectors.toSet())); Optional<ExternalLocationCase> extLoc = EndpointUtils.getExternalLocationFrom(eps.get(0)); builder.put(extLoc.get().getExternalNodeMountPoint().firstKeyOf(Node.class).getNodeId(), new InterfaceKey(extLoc.get().getExternalNodeConnector()), copyOf); ImmutableList<AddressEndpointWithLocation> lisst = ImmutableList.copyOf(eps.stream() .filter(addrEp -> !AddressEndpointUtils.sameExternalLocationCase(eps.get(0), addrEp)) .collect(Collectors.toList())); if (!lisst.isEmpty()) { resolveEndpointsOnMultipleInterface(lisst, builder); } } public @Nonnull static ImmutableSet<AddressEndpointKey> otherEndpointsOnTheSameInterface(@Nonnull PolicyContext ctx, @Nonnull AddressEndpointKey key) { if (multipleEndpointsOnInterface != null) { for (InterfaceKey ifaceKey : multipleEndpointsOnInterface.columnKeySet()) { for (NodeId nodeId : multipleEndpointsOnInterface.column(ifaceKey).keySet()) { ImmutableSet<AddressEndpointKey> addrEps = multipleEndpointsOnInterface.get(nodeId, ifaceKey); if (addrEps != null && addrEps.contains(key) && addrEps.size() > 1) { return multipleEndpointsOnInterface.get(nodeId, ifaceKey); } } } } return ImmutableSet.copyOf(Sets.newHashSet()); } }