From: Robert Varga Date: Wed, 11 Mar 2015 11:26:58 +0000 (+0100) Subject: BUG-2383: split out policies into separate files X-Git-Tag: release/lithium~168^2 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=commitdiff_plain;h=2a7a840046c9dc5999c45b73b48114969c6de83f;p=bgpcep.git BUG-2383: split out policies into separate files Static policies kwill not work, as some of them require additional arguments to perform their role. Split them out, so they can be evolved independently. Change-Id: I33decf294c53391d4d705014adfd62b6d5ffa34d Signed-off-by: Robert Varga --- diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AbstractExportPolicy.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AbstractExportPolicy.java index 8be46f93f0..0432ba9d07 100644 --- a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AbstractExportPolicy.java +++ b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AbstractExportPolicy.java @@ -7,117 +7,14 @@ */ package org.opendaylight.protocol.bgp.rib.impl; -import com.google.common.collect.Maps; -import java.util.EnumMap; -import java.util.Map; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerRole; import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; /** - * Defines the internal hooks invoked when a new route appears. + * Defines the internal hooks invoked when a route is being distributed + * to a peer. */ abstract class AbstractExportPolicy { - // Invoked on routes which we send outside of our home AS - private static final class ToExternalExportPolicy extends AbstractExportPolicy { - @Override - ContainerNode effectiveAttributes(final PeerRole sourceRole, final ContainerNode attributes) { - // FIXME: filter all non-transitive attributes - // FIXME: but that may end up hurting our informedness - // FIXME: prepend local AS to add our AS into AS_PATH - - switch (sourceRole) { - case Ebgp: - // eBGP -> eBGP, propagate - return attributes; - case Ibgp: - // Non-Client iBGP -> eBGP, propagate - return attributes; - case RrClient: - // Client iBGP -> eBGP, propagate - return attributes; - default: - break; - } - return attributes; - } - } - - // Invoked on routes which we send to our normal home AS peers. - private static class ToInternalExportPolicy extends AbstractExportPolicy { - protected static ContainerNode prependClusterId(final ContainerNode attributes) { - // FIXME: prepend local CLUSTER_ID to CLUSTER_LIST - return attributes; - } - - // Attributes when reflecting a route - protected static ContainerNode toClientAttributes(final ContainerNode attributes) { - // TODO: (defensiveness) verify ORIGINATOR_ID (should have been set) - - return prependClusterId(attributes); - } - - @Override - ContainerNode effectiveAttributes(final PeerRole sourceRole, final ContainerNode attributes) { - // Implement filtering according to . - - switch (sourceRole) { - case Ebgp: - // eBGP -> Non-Client iBGP, propagate - return attributes; - - case Ibgp: - // Non-Client iBGP -> Non-Client iBGP, block - return null; - - case RrClient: - // Client iBGP -> Non-Client iBGP, reflect - return toClientAttributes(attributes); - default: - break; - } - return attributes; - } - } - - /** - * Invoked on routes which we send to our reflector peers. This is a special-case of - * FromInternalImportPolicy. - */ - private static final class ToReflectorClientExportPolicy extends AbstractExportPolicy { - @Override - ContainerNode effectiveAttributes(final PeerRole sourceRole, final ContainerNode attributes) { - switch (sourceRole) { - case Ebgp: - // eBGP -> Client iBGP, propagate - return attributes; - case Ibgp: - // Non-Client iBGP -> Client iBGP, reflect - // FIXME: add ORIGINATOR_ID - - return ToInternalExportPolicy.prependClusterId(attributes); - case RrClient: - // Client iBGP -> Client iBGP, reflect - return ToInternalExportPolicy.toClientAttributes(attributes); - default: - throw new IllegalStateException("Unhandled source role " + sourceRole); - } - } - } - - static final Map POLICIES; - - static { - final Map p = new EnumMap(PeerRole.class); - p.put(PeerRole.Ebgp, new ToExternalExportPolicy()); - p.put(PeerRole.Ibgp, new ToInternalExportPolicy()); - p.put(PeerRole.RrClient, new ToReflectorClientExportPolicy()); - POLICIES = Maps.immutableEnumMap(p); - } - - static AbstractExportPolicy forRole(final PeerRole peerRole) { - return POLICIES.get(peerRole); - } - /** * Transform outgoing attributes according to policy. * @@ -126,4 +23,4 @@ abstract class AbstractExportPolicy { * @return Filtered attributes, or null if the advertisement should be ignored. */ abstract ContainerNode effectiveAttributes(PeerRole sourceRole, ContainerNode attributes); -} \ No newline at end of file +} diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AbstractImportPolicy.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AbstractImportPolicy.java index 7851fb3579..9544893a93 100644 --- a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AbstractImportPolicy.java +++ b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AbstractImportPolicy.java @@ -7,58 +7,13 @@ */ package org.opendaylight.protocol.bgp.rib.impl; -import java.util.EnumMap; -import java.util.Map; import javax.annotation.Nullable; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerRole; import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; /** * Defines the internal hooks invoked when a new route appears. */ abstract class AbstractImportPolicy { - // Invoked on routes which we get from outside of our home AS - private static final class FromExternalImportPolicy extends AbstractImportPolicy { - @Override - ContainerNode effectiveAttributes(final ContainerNode attributes) { - // FIXME: filter all non-transitive attributes - // FIXME: but that may end up hurting our informedness - return attributes; - } - } - - // Invoked on routes which we get from our normal home AS peers. - private static class FromInternalImportPolicy extends AbstractImportPolicy { - @Override - ContainerNode effectiveAttributes(final ContainerNode attributes) { - // FIXME: Implement filtering according to . - return attributes; - } - } - - /** - * Invoked on routes which we get from our reflector peers. This is a special-case of - * FromInternalImportPolicy. - * - */ - private static final class FromReflectorClientImportPolicy extends FromInternalImportPolicy { - // FIXME: override if necessary - } - - private static final Map POLICIES; - - static { - final Map p = new EnumMap(PeerRole.class); - p.put(PeerRole.Ebgp, new FromExternalImportPolicy()); - p.put(PeerRole.Ibgp, new FromInternalImportPolicy()); - p.put(PeerRole.RrClient, new FromReflectorClientImportPolicy()); - POLICIES = p; - } - - static AbstractImportPolicy forRole(final PeerRole peerRole) { - return POLICIES.get(peerRole); - } - /** * Transform incoming attributes according to policy. * diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AbstractReflectingExportPolicy.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AbstractReflectingExportPolicy.java new file mode 100644 index 0000000000..a9b2ec9f19 --- /dev/null +++ b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AbstractReflectingExportPolicy.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2015 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.protocol.bgp.rib.impl; + +import com.google.common.base.Preconditions; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.ClusterIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; + +/** + * An intermediate abstract class shared by both Client and Non-Client + * export policies. + */ +abstract class AbstractReflectingExportPolicy extends AbstractExportPolicy { + private final ClusterIdentifier clusterId; + private final Ipv4Address originatorId; + + protected AbstractReflectingExportPolicy(final Ipv4Address originatorId, final ClusterIdentifier clusterId) { + this.originatorId = Preconditions.checkNotNull(originatorId); + this.clusterId = Preconditions.checkNotNull(clusterId); + } + + /** + * Modify attributes so they are updated as per RFC4456 route reflection. + * + * @param attributes Input attributes, may not be null. + * @return Modified (reflected) attributes. + */ + @Nonnull protected final ContainerNode reflectedAttributes(@Nonnull final ContainerNode attributes) { + return AttributeOperations.getInstance(attributes).reflectedAttributes(attributes, originatorId, clusterId); + } +} diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AttributeOperations.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AttributeOperations.java new file mode 100644 index 0000000000..1d1baac704 --- /dev/null +++ b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AttributeOperations.java @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2015 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.protocol.bgp.rib.impl; + +import com.google.common.base.Optional; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.collect.ImmutableList; +import java.util.Collection; +import java.util.Iterator; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.AsPath; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.ClusterId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.OriginatorId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.as.path.Segments; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.ClusterIdentifier; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.as.path.segment.CSegment; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.as.path.segment.c.segment.a.list._case.AList; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.as.path.segment.c.segment.a.list._case.a.list.AsSequence; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.QNameModule; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; +import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; +import org.opendaylight.yangtools.yang.data.api.schema.LeafNode; +import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes; +import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode; +import org.opendaylight.yangtools.yang.data.impl.schema.Builders; +import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Utility class for working with route attributes in binding independent form. An instance + * is always bound to a certain namespace, so it maintains cached attribute identifiers. + */ +final class AttributeOperations { + private static final Logger LOG = LoggerFactory.getLogger(AttributeOperations.class); + private static final LoadingCache ATTRIBUTES_CACHE = CacheBuilder.newBuilder().weakKeys().weakValues().build( + new CacheLoader() { + @Override + public AttributeOperations load(final QNameModule key) throws Exception { + return new AttributeOperations(key); + } + }); + + private final Iterable originatorIdPath; + private final Iterable clusterListPath; + private final NodeIdentifier originatorIdContainer; + private final NodeIdentifier originatorIdLeaf; + private final NodeIdentifier clusterListContainer; + private final NodeIdentifier clusterListLeaf; + private final NodeIdentifier asPathContainer; + private final NodeIdentifier asPathSegments; + private final NodeIdentifier asPathChoice; + private final NodeIdentifier asPathList; + private final NodeIdentifier asPathSequence; + private final NodeIdentifier asPathId; + + private AttributeOperations(final QNameModule namespace) { + this.asPathContainer = new NodeIdentifier(QName.cachedReference(QName.create(namespace, AsPath.QNAME.getLocalName()))); + this.asPathSegments = new NodeIdentifier(QName.cachedReference(QName.create(namespace, Segments.QNAME.getLocalName()))); + this.asPathChoice = new NodeIdentifier(QName.cachedReference(QName.create(namespace, CSegment.QNAME.getLocalName()))); + this.asPathList = new NodeIdentifier(QName.cachedReference(QName.create(namespace, AList.QNAME.getLocalName()))); + this.asPathSequence = new NodeIdentifier(QName.cachedReference(QName.create(namespace, AsSequence.QNAME.getLocalName()))); + this.asPathId = new NodeIdentifier(QName.cachedReference(QName.create(namespace, "as"))); + + this.clusterListContainer = new NodeIdentifier(QName.cachedReference(QName.create(namespace, ClusterId.QNAME.getLocalName()))); + this.clusterListLeaf = new NodeIdentifier(QName.cachedReference(QName.create(namespace, "cluster"))); + this.clusterListPath = ImmutableList.of(clusterListContainer, clusterListLeaf); + this.originatorIdContainer = new NodeIdentifier(QName.cachedReference(QName.create(namespace, OriginatorId.QNAME.getLocalName()))); + this.originatorIdLeaf = new NodeIdentifier(QName.cachedReference(QName.create(namespace, "originator"))); + this.originatorIdPath = ImmutableList.of(originatorIdContainer, originatorIdLeaf); + } + + static AttributeOperations getInstance(final ContainerNode attributes) { + return ATTRIBUTES_CACHE.getUnchecked(QNameModule.cachedReference(attributes.getNodeType().getModule())); + } + + private Collection reusableSequence(final UnkeyedListEntryNode segment) { + final Optional> maybeAsSequence = NormalizedNodes.findNode(segment, asPathChoice, asPathList, asPathSequence); + if (maybeAsSequence.isPresent()) { + final UnkeyedListNode asList = (UnkeyedListNode) maybeAsSequence.get(); + if (asList.getSize() < 255) { + return asList.getValue(); + } + } + + return null; + } + + ContainerNode exportedAttributes(final ContainerNode attributes, final Long localAs) { + final DataContainerNodeAttrBuilder b = Builders.containerBuilder(); + b.withNodeIdentifier(attributes.getIdentifier()); + + // First filter out non-transitive attributes + // FIXME: removes MULTI_EXIT_DISC, too. + spliceTransitives(b, attributes); + + /* + * This is very ugly, as our AS_PATH model is needlessly complex. The top-level container contains a list + * of segments, each of which is a choice containing another list. Both are unkeyed lists, so we need to + * perform a wholesale replace. + */ + final CollectionNodeBuilder sb = Builders.unkeyedListBuilder(); + sb.withNodeIdentifier(asPathSegments); + + final Optional> maybeOldAsSegments = NormalizedNodes.findNode(attributes, asPathContainer, asPathSegments); + if (maybeOldAsSegments.isPresent()) { + // Builder of inner list + final CollectionNodeBuilder ilb = Builders.unkeyedListBuilder(); + ilb.withNodeIdentifier(asPathSegments); + ilb.withChild(Builders.unkeyedListEntryBuilder().withNodeIdentifier(asPathSequence).withChild(ImmutableNodes.leafNode(asPathId, localAs)).build()); + + /* + * We need to check the first entry in the outer list, to check if the choice is a-list. If it is and its + * total number of elements is less than 255, we need to modify that one. Otherwise we need to create a + * new entry. + */ + final Iterator oldAsSegments = ((UnkeyedListNode) maybeOldAsSegments.get()).getValue().iterator(); + final UnkeyedListEntryNode firstSegment = oldAsSegments.next(); + final Collection reusable = reusableSequence(firstSegment); + if (reusable != null) { + for (UnkeyedListEntryNode child : reusable) { + ilb.withChild(child); + } + } + + // Builder of inner container + final DataContainerNodeAttrBuilder icb = Builders.containerBuilder(); + icb.withNodeIdentifier(asPathList); + icb.withChild(ilb.build()); + + // Choice inside the outer list + final DataContainerNodeBuilder ocb = Builders.choiceBuilder(); + ocb.withNodeIdentifier(asPathChoice); + ocb.withChild(icb.build()); + + // Add the new first segment + sb.withChild(Builders.unkeyedListEntryBuilder().withNodeIdentifier(asPathSegments).withChild(ocb.build()).build()); + + // If we did not merge into the original segment, add it + if (reusable == null) { + sb.withChild(firstSegment); + } + + // Add all subsequent segments + while (oldAsSegments.hasNext()) { + sb.withChild(oldAsSegments.next()); + } + } else { + // Segments are completely empty, create a completely new AS_PATH container with + // a single entry + sb.withChild(Builders.unkeyedListEntryBuilder().withNodeIdentifier(asPathSegments).withChild( + Builders.choiceBuilder().withNodeIdentifier(asPathChoice).withChild( + Builders.containerBuilder().withNodeIdentifier(asPathList).withChild( + Builders.unkeyedListBuilder().withNodeIdentifier(asPathSequence).withChild( + Builders.unkeyedListEntryBuilder().withNodeIdentifier(asPathSequence).withChild( + ImmutableNodes.leafNode(asPathId, localAs)).build()).build()).build()).build()).build()); + + } + + b.withChild(Builders.containerBuilder().withNodeIdentifier(asPathContainer).withChild(sb.build()).build()); + return b.build(); + } + + + // Attributes when reflecting a route + ContainerNode reflectedAttributes(final ContainerNode attributes, final Ipv4Address originatorId, final ClusterIdentifier clusterId) { + final DataContainerNodeAttrBuilder b = Builders.containerBuilder(attributes); + + // Create a new CLUSTER_LIST builder + final ListNodeBuilder> clb = Builders.leafSetBuilder(); + clb.withNodeIdentifier(clusterListLeaf); + + // prepend local CLUSTER_ID + clb.withChild(Builders.leafSetEntryBuilder().withValue(clusterId).build()); + + // if there was a CLUSTER_LIST attribute, add all other entries + final Optional> maybeClusterList = NormalizedNodes.findNode(attributes, clusterListPath); + if (maybeClusterList.isPresent()) { + final NormalizedNode clusterList = maybeClusterList.get(); + if (clusterList instanceof LeafSetNode) { + for (LeafSetEntryNode n : ((LeafSetNode)clusterList).getValue()) { + // There's no way we can safely avoid this cast + @SuppressWarnings("unchecked") + final LeafSetEntryNode child = (LeafSetEntryNode)n; + clb.addChild(child); + } + } else { + LOG.warn("Ignoring malformed CLUSTER_LIST {}", clusterList); + } + } else { + LOG.debug("Creating fresh CLUSTER_LIST attribute"); + } + + // Now wrap it in a container and add it to attributes + final DataContainerNodeAttrBuilder cb = Builders.containerBuilder(); + cb.withNodeIdentifier(clusterListContainer); + cb.withChild(clb.build()); + b.withChild(cb.build()); + + // add ORIGINATOR_ID if not present + final Optional> maybeOriginatorId = NormalizedNodes.findNode(attributes, originatorIdPath); + if (!maybeOriginatorId.isPresent()) { + final DataContainerNodeAttrBuilder oib = Builders.containerBuilder(); + oib.withNodeIdentifier(originatorIdContainer); + oib.withChild(ImmutableNodes.leafNode(originatorIdLeaf, originatorId.getValue())); + b.withChild(oib.build()); + } + + return b.build(); + } + + private boolean isTransitiveAttribute(final DataContainerChild child) { + // FIXME: perform a filtering operation + return true; + } + + private boolean spliceTransitives(final DataContainerNodeAttrBuilder target, final ContainerNode attributes) { + // We want to reuse attributes as much as possible, so the result of the loop + // indicates whether we performed a modification. If we have not modified the + // attributes, we can reuse them. + boolean ret = false; + for (DataContainerChild child : attributes.getValue()) { + if (isTransitiveAttribute(child)) { + target.withChild(child); + } else { + ret = true; + } + } + + return ret; + } + + /** + * Filter out all non-transitive attributes. + * + * @param attributes Input attributes + * @return Output attributes, transitive only. + */ + ContainerNode transitiveAttributes(final ContainerNode attributes) { + final DataContainerNodeAttrBuilder b = Builders.containerBuilder(); + b.withNodeIdentifier(attributes.getIdentifier()); + + boolean modified = spliceTransitives(b, attributes); + return modified ? b.build() : attributes; + } + + LeafSetNode getClusterList(final ContainerNode attributes) { + final Optional> maybeClusterList = NormalizedNodes.findNode(attributes, clusterListPath); + if (maybeClusterList.isPresent()) { + final NormalizedNode clusterList = maybeClusterList.get(); + if (clusterList instanceof LeafSetNode) { + return (LeafSetNode) clusterList; + } + + LOG.warn("Unexpected CLUSTER_LIST node {}, ignoring it", clusterList); + } + + return null; + } + + Object getOriginatorId(final ContainerNode attributes) { + final Optional> maybeOriginatorId = NormalizedNodes.findNode(attributes, originatorIdPath); + if (!maybeOriginatorId.isPresent()) { + LOG.debug("No ORIGINATOR_ID present"); + return null; + } + + final NormalizedNode originatorId = maybeOriginatorId.get(); + if (originatorId instanceof LeafNode) { + return ((LeafNode)originatorId).getValue(); + } + + LOG.warn("Unexpected ORIGINATOR_ID node {}, ignoring it", originatorId); + return null; + } + +} diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/EffectiveRibInWriter.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/EffectiveRibInWriter.java index 79cbf77d84..4b61a479b7 100644 --- a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/EffectiveRibInWriter.java +++ b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/EffectiveRibInWriter.java @@ -126,7 +126,9 @@ final class EffectiveRibInWriter implements AutoCloseable { } } - private void processTableChildren(final DOMDataWriteTransaction tx, final RIBSupport ribSupport, final AbstractImportPolicy policy, final YangInstanceIdentifier tablePath, final Collection children) { + private void processTableChildren(final DOMDataWriteTransaction tx, final RIBSupport ribSupport, final NodeIdentifierWithPredicates peerKey, final YangInstanceIdentifier tablePath, final Collection children) { + final AbstractImportPolicy policy = peerPolicyTracker.policyFor(IdentifierUtils.peerId(peerKey)); + for (DataTreeCandidateNode child : children) { switch (child.getModificationType()) { case DELETE: @@ -171,8 +173,7 @@ final class EffectiveRibInWriter implements AutoCloseable { final RIBSupport ribSupport = getRibSupport(tableKey); final YangInstanceIdentifier tablePath = effectiveTablePath(peerKey, tableKey); - final AbstractImportPolicy policy = peerPolicyTracker.policyFor(IdentifierUtils.peerId(peerKey)); - processTableChildren(tx, ribSupport, policy, tablePath, table.getChildNodes()); + processTableChildren(tx, ribSupport, peerKey, tablePath, table.getChildNodes()); } private void writeTable(final DOMDataWriteTransaction tx, final NodeIdentifierWithPredicates peerKey, final NodeIdentifierWithPredicates tableKey, final DataTreeCandidateNode table) { @@ -182,8 +183,7 @@ final class EffectiveRibInWriter implements AutoCloseable { // Create an empty table TableContext.clearTable(tx, ribSupport, tablePath); - final AbstractImportPolicy policy = peerPolicyTracker.policyFor(IdentifierUtils.peerId(peerKey)); - processTableChildren(tx, ribSupport, policy, tablePath, table.getChildNodes()); + processTableChildren(tx, ribSupport, peerKey, tablePath, table.getChildNodes()); } @Override @@ -242,8 +242,8 @@ final class EffectiveRibInWriter implements AutoCloseable { private final AdjInTracker adjInTracker; private EffectiveRibInWriter(final DOMDataTreeChangeService service, final DOMTransactionChain chain, final YangInstanceIdentifier ribId) { - this.peerPolicyTracker = new ImportPolicyPeerTracker(service, ribId); - // FIXME: proper argument + // FIXME: proper arguments + this.peerPolicyTracker = new ImportPolicyPeerTracker(service, ribId, null); this.adjInTracker = new AdjInTracker(service, null, chain, ribId); } diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/ExportPolicyPeerTracker.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/ExportPolicyPeerTracker.java index 8d5e9e669a..561981e4f4 100644 --- a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/ExportPolicyPeerTracker.java +++ b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/ExportPolicyPeerTracker.java @@ -44,12 +44,14 @@ final class ExportPolicyPeerTracker extends AbstractPeerRoleTracker { private final Map peerRoles = new HashMap<>(); private volatile Map groups = Collections.emptyMap(); + private final PolicyDatabase policyDatabase; - protected ExportPolicyPeerTracker(final DOMDataTreeChangeService service, final YangInstanceIdentifier ribId) { + ExportPolicyPeerTracker(final DOMDataTreeChangeService service, final YangInstanceIdentifier ribId, final PolicyDatabase policyDatabase) { super(service, ribId); + this.policyDatabase = Preconditions.checkNotNull(policyDatabase); } - private static Map createGroups(final Map peerPathRoles) { + private Map createGroups(final Map peerPathRoles) { if (peerPathRoles.isEmpty()) { return Collections.emptyMap(); } @@ -67,7 +69,7 @@ final class ExportPolicyPeerTracker extends AbstractPeerRoleTracker { final Map ret = new EnumMap<>(PeerRole.class); for (Entry> e : roleToIds.asMap().entrySet()) { - final AbstractExportPolicy policy = AbstractExportPolicy.forRole(e.getKey()); + final AbstractExportPolicy policy = policyDatabase.exportPolicyForRole(e.getKey()); final Collection> peers = ImmutableList.copyOf(Collections2.transform(e.getValue(), GENERATE_PEERID)); ret.put(e.getKey(), new PeerExportGroup(peers, peerRoles, policy)); diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/FromExternalImportPolicy.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/FromExternalImportPolicy.java new file mode 100644 index 0000000000..3fbbc7790f --- /dev/null +++ b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/FromExternalImportPolicy.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2015 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.protocol.bgp.rib.impl; + +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; + +/** + * Import policy invoked on routes which we get from outside of our home AS. + */ +final class FromExternalImportPolicy extends AbstractImportPolicy { + @Override + ContainerNode effectiveAttributes(final ContainerNode attributes) { + /* + * Filter out non-transitive attributes, so they do not cross inter-AS + * boundaries. + * + * FIXME: to be completely flexible, we need to allow for retaining + * the MED attribute. @see https://tools.ietf.org/html/rfc4271#section-5.1.4. + */ + return AttributeOperations.getInstance(attributes).transitiveAttributes(attributes); + } +} \ No newline at end of file diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/FromInternalImportPolicy.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/FromInternalImportPolicy.java new file mode 100644 index 0000000000..31e0d2ea22 --- /dev/null +++ b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/FromInternalImportPolicy.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2015 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.protocol.bgp.rib.impl; + +import com.google.common.base.Preconditions; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.ClusterIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Invoked on routes which we get from our normal home AS peers. + */ +class FromInternalImportPolicy extends AbstractImportPolicy { + private static final Logger LOG = LoggerFactory.getLogger(FromInternalImportPolicy.class); + private final ClusterIdentifier clusterIdentifier; + private final Ipv4Address bgpIdentifier; + + FromInternalImportPolicy(final Ipv4Address bgpIdentifier, final ClusterIdentifier clusterIdentifier) { + this.bgpIdentifier = Preconditions.checkNotNull(bgpIdentifier); + this.clusterIdentifier = Preconditions.checkNotNull(clusterIdentifier); + } + + @Override + ContainerNode effectiveAttributes(final ContainerNode attributes) { + final AttributeOperations oper = AttributeOperations.getInstance(attributes); + + /* + * This is an implementation of https://tools.ietf.org/html/rfc4456#section-8 + * + * We first check the ORIGINATOR_ID, if present. If it matches our BGP identifier, + * we filter the route. + */ + final Object originatorId = oper.getOriginatorId(attributes); + if (bgpIdentifier.getValue().equals(originatorId)) { + LOG.debug("Filtering route with our ORIGINATOR_ID {}", bgpIdentifier); + return null; + } + + /* + * Second we check CLUSTER_LIST, if present. If it contains our CLUSTER_ID, we issue + * a warning and ignore the route. + */ + final LeafSetNode clusterList = oper.getClusterList(attributes); + if (clusterList != null) { + for (LeafSetEntryNode node : clusterList.getValue()) { + if (clusterIdentifier.getValue().equals(node.getValue())) { + LOG.info("Received a route with our CLUSTER_ID {} in CLUSTER_LIST {}, filtering it", clusterIdentifier.getValue(), clusterList); + return null; + } + } + } + + return attributes; + } +} diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/FromReflectorClientImportPolicy.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/FromReflectorClientImportPolicy.java new file mode 100644 index 0000000000..b50a38eac5 --- /dev/null +++ b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/FromReflectorClientImportPolicy.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2015 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.protocol.bgp.rib.impl; + +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.ClusterIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; + +/** + * Invoked on routes which we get from our reflector peers. This is a special-case of + * FromInternalImportPolicy. + */ +final class FromReflectorClientImportPolicy extends FromInternalImportPolicy { + FromReflectorClientImportPolicy(final Ipv4Address bgpIdentifier, final ClusterIdentifier clusterIdentifier) { + super(bgpIdentifier, clusterIdentifier); + } + + @Override + ContainerNode effectiveAttributes(final ContainerNode attributes) { + // TODO: (defensiveness) verify ORIGINATOR_ID (should have been set) + + return super.effectiveAttributes(attributes); + } +} diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/ImportPolicyPeerTracker.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/ImportPolicyPeerTracker.java index b5f2a00a5b..99e091e76c 100644 --- a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/ImportPolicyPeerTracker.java +++ b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/ImportPolicyPeerTracker.java @@ -7,6 +7,7 @@ */ package org.opendaylight.protocol.bgp.rib.impl; +import com.google.common.base.Preconditions; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService; @@ -20,9 +21,11 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdent */ final class ImportPolicyPeerTracker extends AbstractPeerRoleTracker { private final Map policies = new ConcurrentHashMap<>(); + private final PolicyDatabase policyDatabase; - protected ImportPolicyPeerTracker(final DOMDataTreeChangeService service, final YangInstanceIdentifier ribId) { + protected ImportPolicyPeerTracker(final DOMDataTreeChangeService service, final YangInstanceIdentifier ribId, final PolicyDatabase policyDatabase) { super(service, ribId); + this.policyDatabase = Preconditions.checkNotNull(policyDatabase); } @Override @@ -31,7 +34,7 @@ final class ImportPolicyPeerTracker extends AbstractPeerRoleTracker { if (role != null) { // Lookup policy based on role - final AbstractImportPolicy policy = AbstractImportPolicy.forRole(role); + final AbstractImportPolicy policy = policyDatabase.importPolicyForRole(role); // Update lookup map policies.put(peer, policy); diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/LocRibWriter.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/LocRibWriter.java index 3662ac6037..7c7b564f8e 100644 --- a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/LocRibWriter.java +++ b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/LocRibWriter.java @@ -46,7 +46,7 @@ final class LocRibWriter implements AutoCloseable, DOMDataTreeChangeListener { this.attributesIdentifier = ribSupport.routeAttributesIdentifier(); // FIXME: proper values - this.peerPolicyTracker = new ExportPolicyPeerTracker(null, null); + this.peerPolicyTracker = new ExportPolicyPeerTracker(null, null, null); } @Override diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/PolicyDatabase.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/PolicyDatabase.java new file mode 100644 index 0000000000..199f2c9b5d --- /dev/null +++ b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/PolicyDatabase.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2015 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.protocol.bgp.rib.impl; + +import java.util.EnumMap; +import java.util.Map; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerRole; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.ClusterIdentifier; + +/** + * Policy database attached to a particular RIB instance. Acts as the unified + * lookup point. + */ +final class PolicyDatabase { + private final Map exportPolicies = new EnumMap<>(PeerRole.class); + private final Map importPolicies = new EnumMap<>(PeerRole.class); + + PolicyDatabase(final Long localAs, final Ipv4Address bgpId, final ClusterIdentifier clusterId) { + exportPolicies.put(PeerRole.Ebgp, new ToExternalExportPolicy(localAs)); + exportPolicies.put(PeerRole.Ibgp, new ToInternalExportPolicy(bgpId, clusterId)); + exportPolicies.put(PeerRole.RrClient, new ToReflectorClientExportPolicy(bgpId, clusterId)); + + importPolicies.put(PeerRole.Ebgp, new FromExternalImportPolicy()); + importPolicies.put(PeerRole.Ibgp, new FromInternalImportPolicy(bgpId, clusterId)); + importPolicies.put(PeerRole.RrClient, new FromReflectorClientImportPolicy(bgpId, clusterId)); + } + + AbstractExportPolicy exportPolicyForRole(final PeerRole peerRole) { + return exportPolicies.get(peerRole); + } + + AbstractImportPolicy importPolicyForRole(final PeerRole peerRole) { + return importPolicies.get(peerRole); + } +} diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/ToExternalExportPolicy.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/ToExternalExportPolicy.java new file mode 100644 index 0000000000..ef51e13cca --- /dev/null +++ b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/ToExternalExportPolicy.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2015 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.protocol.bgp.rib.impl; + +import com.google.common.base.Preconditions; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerRole; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; + +/** + * Invoked on routes which we send outside of our home AS. + */ +final class ToExternalExportPolicy extends AbstractExportPolicy { + private final Long localAs; + + ToExternalExportPolicy(final Long localAs) { + this.localAs = Preconditions.checkNotNull(localAs); + } + + @Override + ContainerNode effectiveAttributes(final PeerRole sourceRole, final ContainerNode attributes) { + final ContainerNode ret = AttributeOperations.getInstance(attributes).exportedAttributes(attributes, localAs); + + switch (sourceRole) { + case Ebgp: + // eBGP -> eBGP, propagate + return ret; + case Ibgp: + // Non-Client iBGP -> eBGP, propagate + return ret; + case RrClient: + // Client iBGP -> eBGP, propagate + return ret; + default: + throw new IllegalArgumentException("Unhandled source role " + sourceRole); + } + } +} \ No newline at end of file diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/ToInternalExportPolicy.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/ToInternalExportPolicy.java new file mode 100644 index 0000000000..07e982dcd2 --- /dev/null +++ b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/ToInternalExportPolicy.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2015 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.protocol.bgp.rib.impl; + +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerRole; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.ClusterIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; + +/** + * Invoked on routes which we send to our normal home AS peers. + */ +final class ToInternalExportPolicy extends AbstractReflectingExportPolicy { + ToInternalExportPolicy(final Ipv4Address originatorId, final ClusterIdentifier clusterId) { + super(originatorId, clusterId); + } + + @Override + ContainerNode effectiveAttributes(final PeerRole sourceRole, final ContainerNode attributes) { + switch (sourceRole) { + case Ebgp: + // eBGP -> Non-Client iBGP, propagate + return attributes; + case Ibgp: + // Non-Client iBGP -> Non-Client iBGP, block + return null; + case RrClient: + // Client iBGP -> Non-Client iBGP, reflect + return reflectedAttributes(attributes); + default: + throw new IllegalArgumentException("Unhandled source role " + sourceRole); + } + } +} \ No newline at end of file diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/ToReflectorClientExportPolicy.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/ToReflectorClientExportPolicy.java new file mode 100644 index 0000000000..c4962a40a0 --- /dev/null +++ b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/ToReflectorClientExportPolicy.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2015 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.protocol.bgp.rib.impl; + +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerRole; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.ClusterIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; + +/** + * Invoked on routes which we send to our reflector peers. This is a special-case of + * FromInternalImportPolicy. + */ +final class ToReflectorClientExportPolicy extends AbstractReflectingExportPolicy { + + ToReflectorClientExportPolicy(final Ipv4Address originatorId, final ClusterIdentifier clusterId) { + super(originatorId, clusterId); + } + + @Override + ContainerNode effectiveAttributes(final PeerRole sourceRole, final ContainerNode attributes) { + switch (sourceRole) { + case Ebgp: + // eBGP -> Client iBGP, propagate + return attributes; + case Ibgp: + // Non-Client iBGP -> Client iBGP, reflect + return reflectedAttributes(attributes); + case RrClient: + // Client iBGP -> Client iBGP, reflect + return reflectedAttributes(attributes); + default: + throw new IllegalArgumentException("Unhandled source role " + sourceRole); + } + } +}