BUG-2383: split out policies into separate files 48/16348/12
authorRobert Varga <rovarga@cisco.com>
Wed, 11 Mar 2015 11:26:58 +0000 (12:26 +0100)
committerRobert Varga <rovarga@cisco.com>
Fri, 13 Mar 2015 09:19:44 +0000 (10:19 +0100)
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 <rovarga@cisco.com>
15 files changed:
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AbstractExportPolicy.java
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AbstractImportPolicy.java
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AbstractReflectingExportPolicy.java [new file with mode: 0644]
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AttributeOperations.java [new file with mode: 0644]
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/EffectiveRibInWriter.java
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/ExportPolicyPeerTracker.java
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/FromExternalImportPolicy.java [new file with mode: 0644]
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/FromInternalImportPolicy.java [new file with mode: 0644]
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/FromReflectorClientImportPolicy.java [new file with mode: 0644]
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/ImportPolicyPeerTracker.java
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/LocRibWriter.java
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/PolicyDatabase.java [new file with mode: 0644]
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/ToExternalExportPolicy.java [new file with mode: 0644]
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/ToInternalExportPolicy.java [new file with mode: 0644]
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/ToReflectorClientExportPolicy.java [new file with mode: 0644]

index 8be46f93f0c7e503c4324d072b8b37b18c29f86f..0432ba9d07534e7d195a9bd8c1ade6377328a07a 100644 (file)
  */
 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.
      *
@@ -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
+}
index 7851fb3579d5044ee0408c041a43abf689b79f5f..9544893a935d1755b907a50c7e32916bd24d7a5c 100644 (file)
@@ -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 <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.
      *
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 (file)
index 0000000..a9b2ec9
--- /dev/null
@@ -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 (file)
index 0000000..1d1baac
--- /dev/null
@@ -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<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;
+    }
+
+}
index 79cbf77d8491b716cebd97dab0f72e46579b2c30..4b61a479b7257917ccbf6728800b397ddc89eb2e 100644 (file)
@@ -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<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:
@@ -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);
     }
 
index 8d5e9e669a390dedb171f89a2d82fc2b17f8c24a..561981e4f47f7ba14182533d4685b93ace8fcf1f 100644 (file)
@@ -44,12 +44,14 @@ final class ExportPolicyPeerTracker extends AbstractPeerRoleTracker {
 
     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();
         }
@@ -67,7 +69,7 @@ final class ExportPolicyPeerTracker extends AbstractPeerRoleTracker {
 
         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));
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 (file)
index 0000000..3fbbc77
--- /dev/null
@@ -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 (file)
index 0000000..31e0d2e
--- /dev/null
@@ -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 (file)
index 0000000..b50a38e
--- /dev/null
@@ -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);
+    }
+}
index b5f2a00a5be35258914254ca468c1a2085402b23..99e091e76c9dd63f3d490a0fc46ddd6e38881fc4 100644 (file)
@@ -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<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
@@ -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);
index 3662ac60376e8490717d7a04a29cdea635a65b36..7c7b564f8e6e04c561280ec4f5a1a4c5df37a14b 100644 (file)
@@ -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 (file)
index 0000000..199f2c9
--- /dev/null
@@ -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<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);
+    }
+}
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 (file)
index 0000000..ef51e13
--- /dev/null
@@ -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 (file)
index 0000000..07e982d
--- /dev/null
@@ -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 (file)
index 0000000..c4962a4
--- /dev/null
@@ -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);
+        }
+    }
+}