*/
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 <a ref="http://tools.ietf.org/html/rfc4456#section-8"/>.
-
- 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<PeerRole, AbstractExportPolicy> POLICIES;
-
- static {
- final Map<PeerRole, AbstractExportPolicy> p = new EnumMap<PeerRole, AbstractExportPolicy>(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.
*
* @return Filtered attributes, or null if the advertisement should be ignored.
*/
abstract ContainerNode effectiveAttributes(PeerRole sourceRole, ContainerNode attributes);
-}
\ No newline at end of file
+}
*/
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 <a href="http://tools.ietf.org/html/rfc4456#section-8"/>.
- 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<PeerRole, AbstractImportPolicy> POLICIES;
-
- static {
- final Map<PeerRole, AbstractImportPolicy> p = new EnumMap<PeerRole, AbstractImportPolicy>(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.
*
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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<QNameModule, AttributeOperations> ATTRIBUTES_CACHE = CacheBuilder.newBuilder().weakKeys().weakValues().build(
+ new CacheLoader<QNameModule, AttributeOperations>() {
+ @Override
+ public AttributeOperations load(final QNameModule key) throws Exception {
+ return new AttributeOperations(key);
+ }
+ });
+
+ private final Iterable<PathArgument> originatorIdPath;
+ private final Iterable<PathArgument> 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.<PathArgument>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.<PathArgument>of(originatorIdContainer, originatorIdLeaf);
+ }
+
+ static AttributeOperations getInstance(final ContainerNode attributes) {
+ return ATTRIBUTES_CACHE.getUnchecked(QNameModule.cachedReference(attributes.getNodeType().getModule()));
+ }
+
+ private Collection<UnkeyedListEntryNode> reusableSequence(final UnkeyedListEntryNode segment) {
+ final Optional<NormalizedNode<?, ?>> 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<NodeIdentifier, ContainerNode> 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<UnkeyedListEntryNode, UnkeyedListNode> sb = Builders.unkeyedListBuilder();
+ sb.withNodeIdentifier(asPathSegments);
+
+ final Optional<NormalizedNode<?, ?>> maybeOldAsSegments = NormalizedNodes.findNode(attributes, asPathContainer, asPathSegments);
+ if (maybeOldAsSegments.isPresent()) {
+ // Builder of inner list
+ final CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> 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<UnkeyedListEntryNode> oldAsSegments = ((UnkeyedListNode) maybeOldAsSegments.get()).getValue().iterator();
+ final UnkeyedListEntryNode firstSegment = oldAsSegments.next();
+ final Collection<UnkeyedListEntryNode> reusable = reusableSequence(firstSegment);
+ if (reusable != null) {
+ for (UnkeyedListEntryNode child : reusable) {
+ ilb.withChild(child);
+ }
+ }
+
+ // Builder of inner container
+ final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> icb = Builders.containerBuilder();
+ icb.withNodeIdentifier(asPathList);
+ icb.withChild(ilb.build());
+
+ // Choice inside the outer list
+ final DataContainerNodeBuilder<NodeIdentifier, ChoiceNode> 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<NodeIdentifier, ContainerNode> b = Builders.containerBuilder(attributes);
+
+ // Create a new CLUSTER_LIST builder
+ final ListNodeBuilder<Object, LeafSetEntryNode<Object>> 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<NormalizedNode<?, ?>> 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<Object> child = (LeafSetEntryNode<Object>)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<NodeIdentifier, ContainerNode> cb = Builders.containerBuilder();
+ cb.withNodeIdentifier(clusterListContainer);
+ cb.withChild(clb.build());
+ b.withChild(cb.build());
+
+ // add ORIGINATOR_ID if not present
+ final Optional<NormalizedNode<?, ?>> maybeOriginatorId = NormalizedNodes.findNode(attributes, originatorIdPath);
+ if (!maybeOriginatorId.isPresent()) {
+ final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> 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<? extends PathArgument, ?> child) {
+ // FIXME: perform a filtering operation
+ return true;
+ }
+
+ private boolean spliceTransitives(final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> 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<? extends PathArgument, ?> 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<NodeIdentifier, ContainerNode> b = Builders.containerBuilder();
+ b.withNodeIdentifier(attributes.getIdentifier());
+
+ boolean modified = spliceTransitives(b, attributes);
+ return modified ? b.build() : attributes;
+ }
+
+ LeafSetNode<?> getClusterList(final ContainerNode attributes) {
+ final Optional<NormalizedNode<?, ?>> 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<NormalizedNode<?, ?>> 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;
+ }
+
+}
}
}
- private void processTableChildren(final DOMDataWriteTransaction tx, final RIBSupport ribSupport, final AbstractImportPolicy policy, final YangInstanceIdentifier tablePath, final Collection<DataTreeCandidateNode> children) {
+ private void processTableChildren(final DOMDataWriteTransaction tx, final RIBSupport ribSupport, final NodeIdentifierWithPredicates peerKey, final YangInstanceIdentifier tablePath, final Collection<DataTreeCandidateNode> children) {
+ final AbstractImportPolicy policy = peerPolicyTracker.policyFor(IdentifierUtils.peerId(peerKey));
+
for (DataTreeCandidateNode child : children) {
switch (child.getModificationType()) {
case DELETE:
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) {
// 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
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);
}
private final Map<YangInstanceIdentifier, PeerRole> peerRoles = new HashMap<>();
private volatile Map<PeerRole, PeerExportGroup> 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<PeerRole, PeerExportGroup> createGroups(final Map<YangInstanceIdentifier, PeerRole> peerPathRoles) {
+ private Map<PeerRole, PeerExportGroup> createGroups(final Map<YangInstanceIdentifier, PeerRole> peerPathRoles) {
if (peerPathRoles.isEmpty()) {
return Collections.emptyMap();
}
final Map<PeerRole, PeerExportGroup> ret = new EnumMap<>(PeerRole.class);
for (Entry<PeerRole, Collection<YangInstanceIdentifier>> e : roleToIds.asMap().entrySet()) {
- final AbstractExportPolicy policy = AbstractExportPolicy.forRole(e.getKey());
+ final AbstractExportPolicy policy = policyDatabase.exportPolicyForRole(e.getKey());
final Collection<Entry<PeerId, YangInstanceIdentifier>> peers = ImmutableList.copyOf(Collections2.transform(e.getValue(), GENERATE_PEERID));
ret.put(e.getKey(), new PeerExportGroup(peers, peerRoles, policy));
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
*/
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;
*/
final class ImportPolicyPeerTracker extends AbstractPeerRoleTracker {
private final Map<PeerId, AbstractImportPolicy> 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
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);
this.attributesIdentifier = ribSupport.routeAttributesIdentifier();
// FIXME: proper values
- this.peerPolicyTracker = new ExportPolicyPeerTracker(null, null);
+ this.peerPolicyTracker = new ExportPolicyPeerTracker(null, null, null);
}
@Override
--- /dev/null
+/*
+ * 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<PeerRole, AbstractExportPolicy> exportPolicies = new EnumMap<>(PeerRole.class);
+ private final Map<PeerRole, AbstractImportPolicy> 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);
+ }
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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);
+ }
+ }
+}