2 * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.protocol.bgp.rib.impl;
10 import static java.util.Objects.requireNonNull;
12 import com.google.common.collect.ImmutableMap;
13 import com.google.common.collect.ImmutableSet;
14 import com.google.common.util.concurrent.FluentFuture;
15 import com.google.common.util.concurrent.FutureCallback;
16 import com.google.common.util.concurrent.MoreExecutors;
17 import java.util.Collection;
18 import java.util.List;
20 import java.util.Optional;
22 import java.util.concurrent.ExecutionException;
23 import java.util.concurrent.atomic.LongAdder;
24 import javax.annotation.Nonnull;
25 import javax.annotation.concurrent.GuardedBy;
26 import javax.annotation.concurrent.NotThreadSafe;
27 import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;
28 import org.opendaylight.controller.md.sal.binding.api.ClusteredDataTreeChangeListener;
29 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
30 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
31 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
32 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
33 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
34 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
35 import org.opendaylight.mdsal.common.api.CommitInfo;
36 import org.opendaylight.protocol.bgp.openconfig.spi.BGPTableTypeRegistryConsumer;
37 import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
38 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContextRegistry;
39 import org.opendaylight.protocol.bgp.rib.impl.spi.RibOutRefresh;
40 import org.opendaylight.protocol.bgp.rib.impl.state.peer.PrefixesInstalledCounters;
41 import org.opendaylight.protocol.bgp.rib.impl.state.peer.PrefixesReceivedCounters;
42 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
43 import org.opendaylight.protocol.bgp.rib.spi.policy.BGPRibRoutingPolicy;
44 import org.opendaylight.protocol.bgp.rib.spi.policy.BGPRouteEntryImportParameters;
45 import org.opendaylight.protocol.bgp.route.targetcontrain.spi.ClientRouteTargetContrainCache;
46 import org.opendaylight.protocol.bgp.route.targetcontrain.spi.RouteTargetMembeshipUtil;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.path.attributes.Attributes;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.PeerRole;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.Route;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.Peer;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.PeerKey;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.peer.AdjRibIn;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.peer.EffectiveRibIn;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.Tables;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.TablesBuilder;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.TablesKey;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.tables.Routes;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.route.target.constrain.rev180618.route.target.constrain.routes.route.target.constrain.routes.RouteTargetConstrainRoute;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.Ipv4AddressFamily;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.Ipv6AddressFamily;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.MplsLabeledVpnSubsequentAddressFamily;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.RouteTarget;
63 import org.opendaylight.yangtools.concepts.ListenerRegistration;
64 import org.opendaylight.yangtools.yang.binding.ChildOf;
65 import org.opendaylight.yangtools.yang.binding.ChoiceIn;
66 import org.opendaylight.yangtools.yang.binding.DataObject;
67 import org.opendaylight.yangtools.yang.binding.Identifiable;
68 import org.opendaylight.yangtools.yang.binding.Identifier;
69 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
70 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
71 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
72 import org.slf4j.Logger;
73 import org.slf4j.LoggerFactory;
76 * Implementation of the BGP import policy. Listens on peer's Adj-RIB-In, inspects all inbound
77 * routes in the context of the advertising peer's role and applies the inbound policy.
79 * Inbound policy is applied as follows:
81 * 1) if the peer is an eBGP peer, perform attribute replacement and filtering
82 * 2) check if a route is admissible based on attributes attached to it, as well as the
83 * advertising peer's role
84 * 3) output admitting routes with edited attributes into /bgp-rib/rib/peer/effective-rib-in/tables/routes
87 final class EffectiveRibInWriter implements PrefixesReceivedCounters, PrefixesInstalledCounters,
88 AutoCloseable, ClusteredDataTreeChangeListener<Tables> {
90 private static final Logger LOG = LoggerFactory.getLogger(EffectiveRibInWriter.class);
91 static final NodeIdentifier TABLE_ROUTES = new NodeIdentifier(Routes.QNAME);
92 private static final TablesKey IVP4_VPN_TABLE_KEY = new TablesKey(Ipv4AddressFamily.class,
93 MplsLabeledVpnSubsequentAddressFamily.class);
94 private static final TablesKey IVP6_VPN_TABLE_KEY = new TablesKey(Ipv6AddressFamily.class,
95 MplsLabeledVpnSubsequentAddressFamily.class);
96 private final RIBSupportContextRegistry registry;
97 private final KeyedInstanceIdentifier<Peer, PeerKey> peerIId;
98 private final InstanceIdentifier<EffectiveRibIn> effRibTables;
99 private final DataBroker databroker;
100 private final List<RouteTarget> rtMemberships;
101 private final RibOutRefresh vpnTableRefresher;
102 private final ClientRouteTargetContrainCache rtCache;
103 private ListenerRegistration<?> reg;
104 private BindingTransactionChain chain;
105 private final Map<TablesKey, LongAdder> prefixesReceived;
106 private final Map<TablesKey, LongAdder> prefixesInstalled;
107 private final BGPRibRoutingPolicy ribPolicies;
108 private final BGPRouteEntryImportParameters peerImportParameters;
109 private final BGPTableTypeRegistryConsumer tableTypeRegistry;
111 private FluentFuture<? extends CommitInfo> submitted;
112 private boolean rtMembershipsUpdated;
114 EffectiveRibInWriter(
115 final BGPRouteEntryImportParameters peer,
117 final BindingTransactionChain chain,
118 final KeyedInstanceIdentifier<Peer, PeerKey> peerIId,
119 final Set<TablesKey> tables,
120 final BGPTableTypeRegistryConsumer tableTypeRegistry,
121 final List<RouteTarget> rtMemberships,
122 final ClientRouteTargetContrainCache rtCache) {
123 this.registry = requireNonNull(rib.getRibSupportContext());
124 this.chain = requireNonNull(chain);
125 this.peerIId = requireNonNull(peerIId);
126 this.effRibTables = this.peerIId.child(EffectiveRibIn.class);
127 this.prefixesInstalled = buildPrefixesTables(tables);
128 this.prefixesReceived = buildPrefixesTables(tables);
129 this.ribPolicies = requireNonNull(rib.getRibPolicies());
130 this.databroker = requireNonNull(rib.getDataBroker());
131 this.tableTypeRegistry = requireNonNull(tableTypeRegistry);
132 this.peerImportParameters = peer;
133 this.rtMemberships = rtMemberships;
134 this.rtCache = rtCache;
135 this.vpnTableRefresher = rib;
139 final DataTreeIdentifier<Tables> treeId = new DataTreeIdentifier<>(LogicalDatastoreType.OPERATIONAL,
140 this.peerIId.child(AdjRibIn.class).child(Tables.class));
141 LOG.debug("Registered Effective RIB on {}", this.peerIId);
142 this.reg = requireNonNull(this.databroker).registerDataTreeChangeListener(treeId, this);
145 private static Map<TablesKey, LongAdder> buildPrefixesTables(final Set<TablesKey> tables) {
146 final ImmutableMap.Builder<TablesKey, LongAdder> b = ImmutableMap.builder();
147 tables.forEach(table -> b.put(table, new LongAdder()));
152 @SuppressWarnings("unchecked")
153 public synchronized void onDataTreeChanged(@Nonnull final Collection<DataTreeModification<Tables>> changes) {
154 if (this.chain == null) {
155 LOG.trace("Chain closed. Ignoring Changes : {}", changes);
158 LOG.trace("Data changed called to effective RIB. Change : {}", changes);
159 WriteTransaction tx = null;
160 for (final DataTreeModification<Tables> tc : changes) {
161 final DataObjectModification<Tables> table = tc.getRootNode();
163 tx = this.chain.newWriteOnlyTransaction();
165 final DataObjectModification.ModificationType modificationType = table.getModificationType();
166 switch (modificationType) {
168 final Tables removeTable = table.getDataBefore();
169 final TablesKey tableKey = removeTable.key();
170 final KeyedInstanceIdentifier<Tables, TablesKey> effectiveTablePath
171 = this.effRibTables.child(Tables.class, tableKey);
172 LOG.debug("Delete Effective Table {} modification type {}, "
173 , effectiveTablePath, modificationType);
174 tx.delete(LogicalDatastoreType.OPERATIONAL, effectiveTablePath);
175 CountersUtil.decrement(this.prefixesInstalled.get(tableKey), tableKey);
177 case SUBTREE_MODIFIED:
178 final Tables before = table.getDataBefore();
179 final Tables after = table.getDataAfter();
180 final TablesKey tk = after.key();
181 LOG.debug("Process table {} type {}, dataAfter {}, dataBefore {}",
182 tk, modificationType, after, before);
184 final KeyedInstanceIdentifier<Tables, TablesKey> tablePath
185 = this.effRibTables.child(Tables.class, tk);
186 final RIBSupport ribSupport = this.registry.getRIBSupport(tk);
187 if (ribSupport == null) {
190 tx.put(LogicalDatastoreType.OPERATIONAL,
191 tablePath.child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp
192 .rib.rev180329.rib.tables.Attributes.class), after.getAttributes());
194 final DataObjectModification routesChangesContainer =
195 table.getModifiedChildContainer(ribSupport.routesContainerClass());
197 if (routesChangesContainer == null) {
200 updateRoutes(tx, tk, ribSupport, tablePath, routesChangesContainer.getModifiedChildren());
203 writeTable(tx, table);
206 LOG.warn("Ignoring unhandled root {}", table);
211 final FluentFuture<? extends CommitInfo> future = tx.commit();
212 this.submitted = future;
213 future.addCallback(new FutureCallback<CommitInfo>() {
215 public void onSuccess(final CommitInfo result) {
216 LOG.trace("Successful commit");
220 public void onFailure(final Throwable trw) {
221 LOG.error("Failed commit", trw);
223 }, MoreExecutors.directExecutor());
226 //Refresh VPN Table if RT Memberships were updated
227 if (this.rtMembershipsUpdated) {
228 this.vpnTableRefresher.refreshTable(IVP4_VPN_TABLE_KEY, this.peerImportParameters.getFromPeerId());
229 this.vpnTableRefresher.refreshTable(IVP6_VPN_TABLE_KEY, this.peerImportParameters.getFromPeerId());
230 this.rtMembershipsUpdated = false;
234 @SuppressWarnings("unchecked")
235 private <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
236 R extends Route & ChildOf<? super S> & Identifiable<I>, I extends Identifier<R>> void updateRoutes(
237 final WriteTransaction tx,
238 final TablesKey tableKey, final RIBSupport<C, S, R, I> ribSupport,
239 final KeyedInstanceIdentifier<Tables, TablesKey> tablePath,
240 final Collection<DataObjectModification<R>> routeChanges) {
241 for (final DataObjectModification<R> routeChanged : routeChanges) {
243 = ((InstanceIdentifier.IdentifiableItem<R, I>) routeChanged.getIdentifier()).getKey();
244 switch (routeChanged.getModificationType()) {
245 case SUBTREE_MODIFIED:
247 writeRoutes(tx, tableKey, ribSupport, tablePath, routeKey, routeChanged.getDataAfter());
250 final InstanceIdentifier<R> routeIID = ribSupport.createRouteIdentifier(tablePath, routeKey);
251 tx.delete(LogicalDatastoreType.OPERATIONAL, routeIID);
257 private <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
258 R extends Route & ChildOf<? super S> & Identifiable<I>, I extends Identifier<R>> void writeRoutes(
259 final WriteTransaction tx, final TablesKey tk, final RIBSupport<C, S, R, I> ribSupport,
260 final KeyedInstanceIdentifier<Tables, TablesKey> tablePath, final I routeKey,
262 final InstanceIdentifier<R> routeIID = ribSupport.createRouteIdentifier(tablePath, routeKey);
263 CountersUtil.increment(this.prefixesReceived.get(tk), tk);
264 final Optional<Attributes> effAtt = this.ribPolicies
265 .applyImportPolicies(this.peerImportParameters, route.getAttributes(),
266 tableTypeRegistry.getAfiSafiType(ribSupport.getTablesKey()).get());
267 if (effAtt.isPresent()) {
268 final Optional<RouteTarget> rtMembership = RouteTargetMembeshipUtil.getRT(route);
269 if (rtMembership.isPresent()) {
270 final RouteTarget rt = rtMembership.get();
271 if(PeerRole.Ibgp != this.peerImportParameters.getFromPeerRole()) {
272 this.rtCache.cacheRoute(route);
274 this.rtMemberships.add(rt);
275 this.rtMembershipsUpdated = true;
277 CountersUtil.increment(this.prefixesInstalled.get(tk), tk);
278 tx.put(LogicalDatastoreType.OPERATIONAL, routeIID, route);
279 tx.put(LogicalDatastoreType.OPERATIONAL, routeIID.child(Attributes.class), effAtt.get());
281 final Optional<RouteTarget> rtMembership = RouteTargetMembeshipUtil.getRT(route);
282 if (rtMembership.isPresent()) {
283 if(PeerRole.Ibgp != this.peerImportParameters.getFromPeerRole()) {
284 this.rtCache.uncacheRoute(route);
286 this.rtMemberships.remove(rtMembership.get());
287 this.rtMembershipsUpdated = true;
289 tx.delete(LogicalDatastoreType.OPERATIONAL, routeIID);
293 @SuppressWarnings("unchecked")
294 private void writeTable(final WriteTransaction tx, final DataObjectModification<Tables> table) {
295 final Tables newTable = table.getDataAfter();
296 if (newTable == null) {
299 final TablesKey tableKey = newTable.key();
300 final KeyedInstanceIdentifier<Tables, TablesKey> tablePath
301 = this.effRibTables.child(Tables.class, tableKey);
303 // Create an empty table
304 LOG.trace("Create Empty table", tablePath);
305 if (table.getDataBefore() == null) {
306 tx.put(LogicalDatastoreType.OPERATIONAL, tablePath, new TablesBuilder()
307 .setAfi(tableKey.getAfi()).setSafi(tableKey.getSafi())
308 .setRoutes(this.registry.getRIBSupport(tableKey).emptyRoutesCase())
309 .setAttributes(newTable.getAttributes()).build());
312 final RIBSupport ribSupport = this.registry.getRIBSupport(tableKey);
313 final Routes routes = newTable.getRoutes();
314 if (ribSupport == null || routes == null) {
318 final DataObjectModification routesChangesContainer =
319 table.getModifiedChildContainer(ribSupport.routesContainerClass());
321 if (routesChangesContainer == null) {
324 updateRoutes(tx, tableKey, ribSupport, tablePath, routesChangesContainer.getModifiedChildren());
328 public synchronized void close() {
329 if (this.reg != null) {
333 if (this.submitted != null) {
335 this.submitted.get();
336 } catch (final InterruptedException | ExecutionException throwable) {
337 LOG.error("Write routes failed", throwable);
340 if (this.chain != null) {
344 this.prefixesReceived.values().forEach(LongAdder::reset);
345 this.prefixesInstalled.values().forEach(LongAdder::reset);
349 public long getPrefixedReceivedCount(final TablesKey tablesKey) {
350 final LongAdder counter = this.prefixesReceived.get(tablesKey);
351 if (counter == null) {
354 return counter.longValue();
358 public Set<TablesKey> getTableKeys() {
359 return ImmutableSet.copyOf(this.prefixesReceived.keySet());
363 public boolean isSupported(final TablesKey tablesKey) {
364 return this.prefixesReceived.containsKey(tablesKey);
368 public long getPrefixedInstalledCount(final TablesKey tablesKey) {
369 final LongAdder counter = this.prefixesInstalled.get(tablesKey);
370 if (counter == null) {
373 return counter.longValue();
377 public long getTotalPrefixesInstalled() {
378 return this.prefixesInstalled.values().stream().mapToLong(LongAdder::longValue).sum();