2 * Copyright (c) 2013 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 com.google.common.base.MoreObjects;
11 import com.google.common.base.MoreObjects.ToStringHelper;
12 import com.google.common.base.Optional;
13 import com.google.common.base.Preconditions;
14 import com.google.common.collect.ImmutableSet;
15 import com.google.common.util.concurrent.FutureCallback;
16 import com.google.common.util.concurrent.Futures;
17 import java.util.ArrayList;
18 import java.util.Collections;
19 import java.util.List;
21 import java.util.concurrent.BlockingQueue;
22 import java.util.concurrent.ConcurrentHashMap;
23 import java.util.concurrent.ConcurrentMap;
24 import java.util.concurrent.ExecutionException;
25 import java.util.concurrent.LinkedBlockingQueue;
26 import javax.annotation.concurrent.ThreadSafe;
27 import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;
28 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
29 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
30 import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
31 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
32 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
33 import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
34 import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
35 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
36 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
37 import org.opendaylight.protocol.bgp.rib.DefaultRibReference;
38 import org.opendaylight.protocol.bgp.rib.impl.spi.AdjRIBsOut;
39 import org.opendaylight.protocol.bgp.rib.impl.spi.AdjRIBsOutRegistration;
40 import org.opendaylight.protocol.bgp.rib.impl.spi.BGPDispatcher;
41 import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
42 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContextRegistry;
43 import org.opendaylight.protocol.bgp.rib.spi.AbstractAdjRIBs;
44 import org.opendaylight.protocol.bgp.rib.spi.AdjRIBsIn;
45 import org.opendaylight.protocol.bgp.rib.spi.BGPObjectComparator;
46 import org.opendaylight.protocol.bgp.rib.spi.Peer;
47 import org.opendaylight.protocol.bgp.rib.spi.RIBExtensionConsumerContext;
48 import org.opendaylight.protocol.framework.ReconnectStrategyFactory;
49 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.AsNumber;
50 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
51 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev150305.bgp.rib.rib.loc.rib.tables.routes.Ipv4RoutesCase;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev150305.bgp.rib.rib.loc.rib.tables.routes.Ipv6RoutesCase;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev150305.ipv4.prefixes.DestinationIpv4Builder;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev150305.ipv4.prefixes.destination.ipv4.Ipv4Prefixes;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev150305.ipv4.prefixes.destination.ipv4.Ipv4PrefixesBuilder;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev150305.update.path.attributes.mp.reach.nlri.advertized.routes.destination.type.DestinationIpv4CaseBuilder;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.Update;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.UpdateBuilder;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.update.Nlri;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.update.PathAttributes;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.update.WithdrawnRoutes;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.BgpTableType;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.PathAttributes1;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.PathAttributes2;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.MpReachNlri;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.MpReachNlriBuilder;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.MpUnreachNlri;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.MpUnreachNlriBuilder;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.mp.reach.nlri.AdvertizedRoutesBuilder;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.mp.unreach.nlri.WithdrawnRoutesBuilder;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.BgpRib;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.RibId;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.Route;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.Rib;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.RibBuilder;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.RibKey;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.LocRib;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.LocRibBuilder;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.Tables;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.TablesKey;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.Ipv4AddressFamily;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.UnicastSubsequentAddressFamily;
84 import org.opendaylight.yangtools.binding.data.codec.api.BindingCodecTreeFactory;
85 import org.opendaylight.yangtools.sal.binding.generator.impl.GeneratedClassLoadingStrategy;
86 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
87 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
88 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
89 import org.slf4j.Logger;
90 import org.slf4j.LoggerFactory;
93 public final class RIBImpl extends DefaultRibReference implements AutoCloseable, RIB, TransactionChainListener, SchemaContextListener {
94 private static final Logger LOG = LoggerFactory.getLogger(RIBImpl.class);
95 private static final Update EOR = new UpdateBuilder().build();
96 private static final TablesKey IPV4_UNICAST_TABLE = new TablesKey(Ipv4AddressFamily.class, UnicastSubsequentAddressFamily.class);
99 * FIXME: performance: this needs to be turned into a Peer->offset map.
100 * The offset is used to locate a the per-peer state entry in the
103 * For the first release, that map is updated whenever configuration
104 * changes and remains constant on peer flaps. On re-configuration
105 * a resize task is scheduled, so large tables may take some time
106 * before they continue reacting to updates.
108 * For subsequent releases, if we make the reformat process concurrent,
109 * we can trigger reformats when Graceful Restart Time expires for a
112 private final ConcurrentMap<Peer, AdjRIBsOut> ribOuts = new ConcurrentHashMap<>();
113 private final ReconnectStrategyFactory tcpStrategyFactory;
114 private final ReconnectStrategyFactory sessionStrategyFactory;
117 * BGP Best Path selection comparator for ingress best path selection.
119 private final BGPObjectComparator comparator;
120 private final BGPDispatcher dispatcher;
121 private final BindingTransactionChain chain;
122 private final AsNumber localAs;
123 private final Ipv4Address bgpIdentifier;
124 private final Ipv4Address clusterId;
125 private final Set<BgpTableType> localTables;
126 private final RIBTables tables;
127 private final BlockingQueue<Peer> peers;
128 private final DataBroker dataBroker;
129 private final DOMDataBroker domDataBroker;
130 private final RIBExtensionConsumerContext extensions;
132 private final RIBSupportContextRegistryImpl ribContextRegistry;
134 private final Runnable scheduler = new Runnable() {
138 final Peer peer = RIBImpl.this.peers.take();
139 LOG.debug("Advertizing loc-rib to new peer {}.", peer);
140 for (final BgpTableType key : RIBImpl.this.localTables) {
142 synchronized (RIBImpl.this) {
143 final AdjRIBsTransactionImpl trans = new AdjRIBsTransactionImpl(RIBImpl.this.ribOuts, RIBImpl.this.comparator, RIBImpl.this.chain.newWriteOnlyTransaction());
144 final AbstractAdjRIBs<?, ?, ?> adj = (AbstractAdjRIBs<?, ?, ?>) RIBImpl.this.tables.get(new TablesKey(key.getAfi(), key.getSafi()));
145 adj.addAllEntries(trans);
146 Futures.addCallback(trans.commit(), new FutureCallback<Void>() {
148 public void onSuccess(final Void result) {
149 LOG.trace("Advertizing {} to peer {} committed successfully", key.getAfi(), peer);
152 public void onFailure(final Throwable t) {
153 LOG.error("Failed to update peer {} with RIB {}", peer, t);
158 } catch (final InterruptedException e) {
159 LOG.info("Scheduler thread was interrupted.", e);
164 public RIBImpl(final RibId ribId, final AsNumber localAs, final Ipv4Address localBgpId, final Ipv4Address clusterId, final RIBExtensionConsumerContext extensions,
165 final BGPDispatcher dispatcher, final ReconnectStrategyFactory tcpStrategyFactory, final BindingCodecTreeFactory codecFactory,
166 final ReconnectStrategyFactory sessionStrategyFactory, final DataBroker dps, final DOMDataBroker domDataBroker, final List<BgpTableType> localTables, GeneratedClassLoadingStrategy classStrategy) {
167 super(InstanceIdentifier.create(BgpRib.class).child(Rib.class, new RibKey(Preconditions.checkNotNull(ribId))));
168 this.chain = dps.createTransactionChain(this);
169 this.localAs = Preconditions.checkNotNull(localAs);
170 this.comparator = new BGPObjectComparator(localAs);
171 this.bgpIdentifier = Preconditions.checkNotNull(localBgpId);
172 this.clusterId = clusterId == null ? localBgpId : clusterId;
173 this.dispatcher = Preconditions.checkNotNull(dispatcher);
174 this.sessionStrategyFactory = Preconditions.checkNotNull(sessionStrategyFactory);
175 this.tcpStrategyFactory = Preconditions.checkNotNull(tcpStrategyFactory);
176 this.localTables = ImmutableSet.copyOf(localTables);
177 this.tables = new RIBTables(extensions);
178 this.peers = new LinkedBlockingQueue<>();
179 this.dataBroker = dps;
180 this.domDataBroker = Preconditions.checkNotNull(domDataBroker);
181 this.extensions = Preconditions.checkNotNull(extensions);
182 this.ribContextRegistry = RIBSupportContextRegistryImpl.create(extensions,codecFactory, classStrategy);
184 LOG.debug("Instantiating RIB table {} at {}", ribId, getInstanceIdentifier());
186 final WriteTransaction trans = this.chain.newWriteOnlyTransaction();
188 // put empty BgpRib if not exists
189 trans.put(LogicalDatastoreType.OPERATIONAL, getInstanceIdentifier(),
190 new RibBuilder().setKey(new RibKey(ribId)).setPeer(Collections.<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.Peer> emptyList()).setId(ribId).setLocRib(
191 new LocRibBuilder().setTables(Collections.<Tables> emptyList()).build()).build(), true);
193 for (final BgpTableType t : localTables) {
194 final TablesKey key = new TablesKey(t.getAfi(), t.getSafi());
195 if (this.tables.create(trans, this, key) == null) {
196 LOG.debug("Did not create local table for unhandled table type {}", t);
200 Futures.addCallback(trans.submit(), new FutureCallback<Void>() {
202 public void onSuccess(final Void result) {
203 LOG.trace("Change committed successfully");
207 public void onFailure(final Throwable t) {
208 LOG.error("Failed to initiate RIB {}", getInstanceIdentifier(), t);
213 synchronized void initTables(final byte[] remoteBgpId) {
217 public synchronized void updateTables(final Peer peer, final Update message) {
218 final AdjRIBsTransactionImpl trans = new AdjRIBsTransactionImpl(this.ribOuts, this.comparator, this.chain.newWriteOnlyTransaction());
220 if (!EOR.equals(message)) {
221 final WithdrawnRoutes wr = message.getWithdrawnRoutes();
223 final AdjRIBsIn<?, ?> ari = this.tables.get(IPV4_UNICAST_TABLE);
226 * create MPUnreach for the routes to be handled in the same way as linkstate routes
228 final List<Ipv4Prefixes> prefixes = new ArrayList<>();
229 for (final Ipv4Prefix p : wr.getWithdrawnRoutes()) {
230 prefixes.add(new Ipv4PrefixesBuilder().setPrefix(p).build());
235 new MpUnreachNlriBuilder().setAfi(Ipv4AddressFamily.class).setSafi(UnicastSubsequentAddressFamily.class).setWithdrawnRoutes(
236 new WithdrawnRoutesBuilder().setDestinationType(
237 new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev150305.update.path.attributes.mp.unreach.nlri.withdrawn.routes.destination.type.DestinationIpv4CaseBuilder().setDestinationIpv4(
238 new DestinationIpv4Builder().setIpv4Prefixes(prefixes).build()).build()).build()).build());
240 LOG.debug("Not removing objects from unhandled IPv4 Unicast");
244 final PathAttributes attrs = message.getPathAttributes();
246 final PathAttributes2 mpu = attrs.getAugmentation(PathAttributes2.class);
248 final MpUnreachNlri nlri = mpu.getMpUnreachNlri();
249 final AdjRIBsIn<?, ?> ari = this.tables.get(new TablesKey(nlri.getAfi(), nlri.getSafi()));
250 // EOR messages do not contain withdrawn routes
251 if (nlri.getWithdrawnRoutes() != null) {
253 ari.removeRoutes(trans, peer, nlri);
255 LOG.debug("Not removing objects from unhandled NLRI {}", nlri);
258 ari.markUptodate(trans, peer);
263 final Nlri ar = message.getNlri();
265 final AdjRIBsIn<?, ?> ari = this.tables.get(IPV4_UNICAST_TABLE);
268 * create MPReach for the routes to be handled in the same way as linkstate routes
270 final List<Ipv4Prefixes> prefixes = new ArrayList<>();
271 for (final Ipv4Prefix p : ar.getNlri()) {
272 prefixes.add(new Ipv4PrefixesBuilder().setPrefix(p).build());
274 final MpReachNlriBuilder b = new MpReachNlriBuilder().setAfi(Ipv4AddressFamily.class).setSafi(
275 UnicastSubsequentAddressFamily.class).setAdvertizedRoutes(
276 new AdvertizedRoutesBuilder().setDestinationType(
277 new DestinationIpv4CaseBuilder().setDestinationIpv4(
278 new DestinationIpv4Builder().setIpv4Prefixes(prefixes).build()).build()).build());
280 b.setCNextHop(attrs.getCNextHop());
283 ari.addRoutes(trans, peer, b.build(), attrs);
285 LOG.debug("Not adding objects from unhandled IPv4 Unicast");
290 final PathAttributes1 mpr = attrs.getAugmentation(PathAttributes1.class);
292 final MpReachNlri nlri = mpr.getMpReachNlri();
294 final AdjRIBsIn<?, ?> ari = this.tables.get(new TablesKey(nlri.getAfi(), nlri.getSafi()));
296 if (message.equals(ari.endOfRib())) {
297 ari.markUptodate(trans, peer);
299 ari.addRoutes(trans, peer, nlri, attrs);
302 LOG.debug("Not adding objects from unhandled NLRI {}", nlri);
307 final AdjRIBsIn<?, ?> ari = this.tables.get(IPV4_UNICAST_TABLE);
309 ari.markUptodate(trans, peer);
311 LOG.debug("End-of-RIB for IPv4 Unicast ignored");
315 Futures.addCallback(trans.commit(), new FutureCallback<Void>() {
317 public void onSuccess(final Void result) {
318 LOG.debug("RIB modification successfully committed.");
322 public void onFailure(final Throwable t) {
323 LOG.error("Failed to commit RIB modification", t);
329 public synchronized void clearTable(final Peer peer, final TablesKey key) {
330 final AdjRIBsIn<?, ?> ari = this.tables.get(key);
332 final AdjRIBsTransactionImpl trans = new AdjRIBsTransactionImpl(this.ribOuts, this.comparator, this.chain.newWriteOnlyTransaction());
333 ari.clear(trans, peer);
335 Futures.addCallback(trans.commit(), new FutureCallback<Void>() {
337 public void onSuccess(final Void result) {
338 LOG.trace("Table {} cleared successfully", key);
342 public void onFailure(final Throwable t) {
343 LOG.error("Failed to clear table {}", key, t);
350 public String toString() {
351 return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
354 protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
355 return toStringHelper;
358 @SuppressWarnings("unchecked")
359 protected <K, V extends Route> AdjRIBsIn<K, V> getTable(final TablesKey key) {
360 return (AdjRIBsIn<K, V>) this.tables.get(key);
364 public synchronized void close() throws InterruptedException, ExecutionException {
365 final WriteTransaction t = this.chain.newWriteOnlyTransaction();
366 t.delete(LogicalDatastoreType.OPERATIONAL, getInstanceIdentifier());
372 public AsNumber getLocalAs() {
377 public Ipv4Address getBgpIdentifier() {
378 return this.bgpIdentifier;
382 public Set<? extends BgpTableType> getLocalTables() {
383 return this.localTables;
387 public ReconnectStrategyFactory getTcpStrategyFactory() {
388 return this.tcpStrategyFactory;
392 public ReconnectStrategyFactory getSessionStrategyFactory() {
393 return this.sessionStrategyFactory;
397 public BGPDispatcher getDispatcher() {
398 return this.dispatcher;
402 public void initTable(final Peer bgpPeer, final TablesKey key) {
403 // FIXME: BUG-196: support graceful restart
407 public AdjRIBsOutRegistration registerRIBsOut(final Peer peer, final AdjRIBsOut aro) {
408 final AdjRIBsOutRegistration reg = new AdjRIBsOutRegistration(aro) {
410 protected void removeRegistration() {
411 RIBImpl.this.ribOuts.remove(peer, aro);
415 this.ribOuts.put(peer, aro);
416 LOG.debug("Registering this peer {} to RIB-Out {}", peer, this.ribOuts);
418 this.peers.put(peer);
419 new Thread(this.scheduler).start();
420 } catch (final InterruptedException e) {
427 public void onTransactionChainFailed(final TransactionChain<?, ?> chain, final AsyncTransaction<?, ?> transaction, final Throwable cause) {
428 LOG.error("Broken chain in RIB {} transaction {}", getInstanceIdentifier(), transaction.getIdentifier(), cause);
432 public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
433 LOG.info("RIB {} closed successfully", getInstanceIdentifier());
437 public long getRoutesCount(final TablesKey key) {
439 final Optional<Tables> tableMaybe = this.dataBroker.newReadOnlyTransaction().read(LogicalDatastoreType.OPERATIONAL,
440 getInstanceIdentifier().child(LocRib.class).child(Tables.class, key)).checkedGet();
441 if (tableMaybe.isPresent()) {
442 final Tables table = tableMaybe.get();
443 if (table.getRoutes() instanceof Ipv4RoutesCase) {
444 final Ipv4RoutesCase routesCase = (Ipv4RoutesCase) table.getRoutes();
445 if (routesCase.getIpv4Routes() != null && routesCase.getIpv4Routes().getIpv4Route() != null) {
446 return routesCase.getIpv4Routes().getIpv4Route().size();
448 } else if (table.getRoutes() instanceof Ipv6RoutesCase) {
449 final Ipv6RoutesCase routesCase = (Ipv6RoutesCase) table.getRoutes();
450 if (routesCase.getIpv6Routes() != null && routesCase.getIpv6Routes().getIpv6Route() != null) {
451 return routesCase.getIpv6Routes().getIpv6Route().size();
455 } catch (final ReadFailedException e) {
462 public DOMTransactionChain createPeerChain(final TransactionChainListener listener) {
463 return this.domDataBroker.createTransactionChain(listener);
467 public RIBExtensionConsumerContext getRibExtensions() {
468 return this.extensions;
472 public RIBSupportContextRegistry getRibSupportContext() {
473 return this.ribContextRegistry;
477 public void onGlobalContextUpdated(final SchemaContext context) {
478 this.ribContextRegistry.onSchemaContextUpdated(context);