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.spi.AbstractAdjRIBs;
43 import org.opendaylight.protocol.bgp.rib.spi.AdjRIBsIn;
44 import org.opendaylight.protocol.bgp.rib.spi.BGPObjectComparator;
45 import org.opendaylight.protocol.bgp.rib.spi.Peer;
46 import org.opendaylight.protocol.bgp.rib.spi.RIBExtensionConsumerContext;
47 import org.opendaylight.protocol.framework.ReconnectStrategyFactory;
48 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.AsNumber;
49 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
50 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev150305.bgp.rib.rib.loc.rib.tables.routes.Ipv4RoutesCase;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev150305.bgp.rib.rib.loc.rib.tables.routes.Ipv6RoutesCase;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev150305.ipv4.prefixes.DestinationIpv4Builder;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev150305.ipv4.prefixes.destination.ipv4.Ipv4Prefixes;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev150305.ipv4.prefixes.destination.ipv4.Ipv4PrefixesBuilder;
56 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;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.Update;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.UpdateBuilder;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.update.Nlri;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.update.PathAttributes;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.update.WithdrawnRoutes;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.BgpTableType;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.PathAttributes1;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.PathAttributes2;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.MpReachNlri;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.MpReachNlriBuilder;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.MpUnreachNlri;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.MpUnreachNlriBuilder;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.mp.reach.nlri.AdvertizedRoutesBuilder;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.mp.unreach.nlri.WithdrawnRoutesBuilder;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.BgpRib;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.RibId;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.Route;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.Rib;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.RibBuilder;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.RibKey;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.LocRib;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.LocRibBuilder;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.Tables;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.TablesKey;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.Ipv4AddressFamily;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.UnicastSubsequentAddressFamily;
83 import org.opendaylight.yangtools.binding.data.codec.api.BindingCodecTreeFactory;
84 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
85 import org.slf4j.Logger;
86 import org.slf4j.LoggerFactory;
89 public final class RIBImpl extends DefaultRibReference implements AutoCloseable, RIB, TransactionChainListener {
90 private static final Logger LOG = LoggerFactory.getLogger(RIBImpl.class);
91 private static final Update EOR = new UpdateBuilder().build();
92 private static final TablesKey IPV4_UNICAST_TABLE = new TablesKey(Ipv4AddressFamily.class, UnicastSubsequentAddressFamily.class);
95 * FIXME: performance: this needs to be turned into a Peer->offset map.
96 * The offset is used to locate a the per-peer state entry in the
99 * For the first release, that map is updated whenever configuration
100 * changes and remains constant on peer flaps. On re-configuration
101 * a resize task is scheduled, so large tables may take some time
102 * before they continue reacting to updates.
104 * For subsequent releases, if we make the reformat process concurrent,
105 * we can trigger reformats when Graceful Restart Time expires for a
108 private final ConcurrentMap<Peer, AdjRIBsOut> ribOuts = new ConcurrentHashMap<>();
109 private final ReconnectStrategyFactory tcpStrategyFactory;
110 private final ReconnectStrategyFactory sessionStrategyFactory;
113 * BGP Best Path selection comparator for ingress best path selection.
115 private final BGPObjectComparator comparator;
116 private final BGPDispatcher dispatcher;
117 private final BindingTransactionChain chain;
118 private final AsNumber localAs;
119 private final Ipv4Address bgpIdentifier;
120 private final Ipv4Address clusterId;
121 private final Set<BgpTableType> localTables;
122 private final RIBTables tables;
123 private final BlockingQueue<Peer> peers;
124 private final DataBroker dataBroker;
125 private final DOMDataBroker domDataBroker;
126 private final RIBExtensionConsumerContext extensions;
127 private final BindingCodecTreeFactory codecFactory;
129 private final Runnable scheduler = new Runnable() {
133 final Peer peer = RIBImpl.this.peers.take();
134 LOG.debug("Advertizing loc-rib to new peer {}.", peer);
135 for (final BgpTableType key : RIBImpl.this.localTables) {
137 synchronized (RIBImpl.this) {
138 final AdjRIBsTransactionImpl trans = new AdjRIBsTransactionImpl(RIBImpl.this.ribOuts, RIBImpl.this.comparator, RIBImpl.this.chain.newWriteOnlyTransaction());
139 final AbstractAdjRIBs<?, ?, ?> adj = (AbstractAdjRIBs<?, ?, ?>) RIBImpl.this.tables.get(new TablesKey(key.getAfi(), key.getSafi()));
140 adj.addAllEntries(trans);
141 Futures.addCallback(trans.commit(), new FutureCallback<Void>() {
143 public void onSuccess(final Void result) {
144 LOG.trace("Advertizing {} to peer {} committed successfully", key.getAfi(), peer);
147 public void onFailure(final Throwable t) {
148 LOG.error("Failed to update peer {} with RIB {}", peer, t);
153 } catch (final InterruptedException e) {
154 LOG.info("Scheduler thread was interrupted.", e);
159 public RIBImpl(final RibId ribId, final AsNumber localAs, final Ipv4Address localBgpId, final Ipv4Address clusterId, final RIBExtensionConsumerContext extensions,
160 final BGPDispatcher dispatcher, final ReconnectStrategyFactory tcpStrategyFactory, final BindingCodecTreeFactory codecFactory,
161 final ReconnectStrategyFactory sessionStrategyFactory, final DataBroker dps, final DOMDataBroker domDataBroker, final List<BgpTableType> localTables) {
162 super(InstanceIdentifier.create(BgpRib.class).child(Rib.class, new RibKey(Preconditions.checkNotNull(ribId))));
163 this.chain = dps.createTransactionChain(this);
164 this.localAs = Preconditions.checkNotNull(localAs);
165 this.comparator = new BGPObjectComparator(localAs);
166 this.bgpIdentifier = Preconditions.checkNotNull(localBgpId);
167 this.clusterId = clusterId == null ? localBgpId : clusterId;
168 this.dispatcher = Preconditions.checkNotNull(dispatcher);
169 this.sessionStrategyFactory = Preconditions.checkNotNull(sessionStrategyFactory);
170 this.tcpStrategyFactory = Preconditions.checkNotNull(tcpStrategyFactory);
171 this.localTables = ImmutableSet.copyOf(localTables);
172 this.tables = new RIBTables(extensions);
173 this.peers = new LinkedBlockingQueue<>();
174 this.dataBroker = dps;
175 this.domDataBroker = Preconditions.checkNotNull(domDataBroker);
176 this.extensions = Preconditions.checkNotNull(extensions);
177 this.codecFactory = codecFactory;
179 LOG.debug("Instantiating RIB table {} at {}", ribId, getInstanceIdentifier());
181 final WriteTransaction trans = this.chain.newWriteOnlyTransaction();
183 // put empty BgpRib if not exists
184 trans.put(LogicalDatastoreType.OPERATIONAL, getInstanceIdentifier(),
185 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(
186 new LocRibBuilder().setTables(Collections.<Tables> emptyList()).build()).build(), true);
188 for (final BgpTableType t : localTables) {
189 final TablesKey key = new TablesKey(t.getAfi(), t.getSafi());
190 if (this.tables.create(trans, this, key) == null) {
191 LOG.debug("Did not create local table for unhandled table type {}", t);
195 Futures.addCallback(trans.submit(), new FutureCallback<Void>() {
197 public void onSuccess(final Void result) {
198 LOG.trace("Change committed successfully");
202 public void onFailure(final Throwable t) {
203 LOG.error("Failed to initiate RIB {}", getInstanceIdentifier(), t);
208 synchronized void initTables(final byte[] remoteBgpId) {
212 public synchronized void updateTables(final Peer peer, final Update message) {
213 final AdjRIBsTransactionImpl trans = new AdjRIBsTransactionImpl(this.ribOuts, this.comparator, this.chain.newWriteOnlyTransaction());
215 if (!EOR.equals(message)) {
216 final WithdrawnRoutes wr = message.getWithdrawnRoutes();
218 final AdjRIBsIn<?, ?> ari = this.tables.get(IPV4_UNICAST_TABLE);
221 * create MPUnreach for the routes to be handled in the same way as linkstate routes
223 final List<Ipv4Prefixes> prefixes = new ArrayList<>();
224 for (final Ipv4Prefix p : wr.getWithdrawnRoutes()) {
225 prefixes.add(new Ipv4PrefixesBuilder().setPrefix(p).build());
230 new MpUnreachNlriBuilder().setAfi(Ipv4AddressFamily.class).setSafi(UnicastSubsequentAddressFamily.class).setWithdrawnRoutes(
231 new WithdrawnRoutesBuilder().setDestinationType(
232 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(
233 new DestinationIpv4Builder().setIpv4Prefixes(prefixes).build()).build()).build()).build());
235 LOG.debug("Not removing objects from unhandled IPv4 Unicast");
239 final PathAttributes attrs = message.getPathAttributes();
241 final PathAttributes2 mpu = attrs.getAugmentation(PathAttributes2.class);
243 final MpUnreachNlri nlri = mpu.getMpUnreachNlri();
244 final AdjRIBsIn<?, ?> ari = this.tables.get(new TablesKey(nlri.getAfi(), nlri.getSafi()));
245 // EOR messages do not contain withdrawn routes
246 if (nlri.getWithdrawnRoutes() != null) {
248 ari.removeRoutes(trans, peer, nlri);
250 LOG.debug("Not removing objects from unhandled NLRI {}", nlri);
253 ari.markUptodate(trans, peer);
258 final Nlri ar = message.getNlri();
260 final AdjRIBsIn<?, ?> ari = this.tables.get(IPV4_UNICAST_TABLE);
263 * create MPReach for the routes to be handled in the same way as linkstate routes
265 final List<Ipv4Prefixes> prefixes = new ArrayList<>();
266 for (final Ipv4Prefix p : ar.getNlri()) {
267 prefixes.add(new Ipv4PrefixesBuilder().setPrefix(p).build());
269 final MpReachNlriBuilder b = new MpReachNlriBuilder().setAfi(Ipv4AddressFamily.class).setSafi(
270 UnicastSubsequentAddressFamily.class).setAdvertizedRoutes(
271 new AdvertizedRoutesBuilder().setDestinationType(
272 new DestinationIpv4CaseBuilder().setDestinationIpv4(
273 new DestinationIpv4Builder().setIpv4Prefixes(prefixes).build()).build()).build());
275 b.setCNextHop(attrs.getCNextHop());
278 ari.addRoutes(trans, peer, b.build(), attrs);
280 LOG.debug("Not adding objects from unhandled IPv4 Unicast");
285 final PathAttributes1 mpr = attrs.getAugmentation(PathAttributes1.class);
287 final MpReachNlri nlri = mpr.getMpReachNlri();
289 final AdjRIBsIn<?, ?> ari = this.tables.get(new TablesKey(nlri.getAfi(), nlri.getSafi()));
291 if (message.equals(ari.endOfRib())) {
292 ari.markUptodate(trans, peer);
294 ari.addRoutes(trans, peer, nlri, attrs);
297 LOG.debug("Not adding objects from unhandled NLRI {}", nlri);
302 final AdjRIBsIn<?, ?> ari = this.tables.get(IPV4_UNICAST_TABLE);
304 ari.markUptodate(trans, peer);
306 LOG.debug("End-of-RIB for IPv4 Unicast ignored");
310 Futures.addCallback(trans.commit(), new FutureCallback<Void>() {
312 public void onSuccess(final Void result) {
313 LOG.debug("RIB modification successfully committed.");
317 public void onFailure(final Throwable t) {
318 LOG.error("Failed to commit RIB modification", t);
324 public synchronized void clearTable(final Peer peer, final TablesKey key) {
325 final AdjRIBsIn<?, ?> ari = this.tables.get(key);
327 final AdjRIBsTransactionImpl trans = new AdjRIBsTransactionImpl(this.ribOuts, this.comparator, this.chain.newWriteOnlyTransaction());
328 ari.clear(trans, peer);
330 Futures.addCallback(trans.commit(), new FutureCallback<Void>() {
332 public void onSuccess(final Void result) {
333 LOG.trace("Table {} cleared successfully", key);
337 public void onFailure(final Throwable t) {
338 LOG.error("Failed to clear table {}", key, t);
345 public String toString() {
346 return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
349 protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
350 return toStringHelper;
353 @SuppressWarnings("unchecked")
354 protected <K, V extends Route> AdjRIBsIn<K, V> getTable(final TablesKey key) {
355 return (AdjRIBsIn<K, V>) this.tables.get(key);
359 public synchronized void close() throws InterruptedException, ExecutionException {
360 final WriteTransaction t = this.chain.newWriteOnlyTransaction();
361 t.delete(LogicalDatastoreType.OPERATIONAL, getInstanceIdentifier());
367 public AsNumber getLocalAs() {
372 public Ipv4Address getBgpIdentifier() {
373 return this.bgpIdentifier;
377 public Set<? extends BgpTableType> getLocalTables() {
378 return this.localTables;
382 public ReconnectStrategyFactory getTcpStrategyFactory() {
383 return this.tcpStrategyFactory;
387 public ReconnectStrategyFactory getSessionStrategyFactory() {
388 return this.sessionStrategyFactory;
392 public BGPDispatcher getDispatcher() {
393 return this.dispatcher;
397 public void initTable(final Peer bgpPeer, final TablesKey key) {
398 // FIXME: BUG-196: support graceful restart
402 public AdjRIBsOutRegistration registerRIBsOut(final Peer peer, final AdjRIBsOut aro) {
403 final AdjRIBsOutRegistration reg = new AdjRIBsOutRegistration(aro) {
405 protected void removeRegistration() {
406 RIBImpl.this.ribOuts.remove(peer, aro);
410 this.ribOuts.put(peer, aro);
411 LOG.debug("Registering this peer {} to RIB-Out {}", peer, this.ribOuts);
413 this.peers.put(peer);
414 new Thread(this.scheduler).start();
415 } catch (final InterruptedException e) {
422 public void onTransactionChainFailed(final TransactionChain<?, ?> chain, final AsyncTransaction<?, ?> transaction, final Throwable cause) {
423 LOG.error("Broken chain in RIB {} transaction {}", getInstanceIdentifier(), transaction.getIdentifier(), cause);
427 public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
428 LOG.info("RIB {} closed successfully", getInstanceIdentifier());
432 public long getRoutesCount(final TablesKey key) {
434 final Optional<Tables> tableMaybe = this.dataBroker.newReadOnlyTransaction().read(LogicalDatastoreType.OPERATIONAL,
435 getInstanceIdentifier().child(LocRib.class).child(Tables.class, key)).checkedGet();
436 if (tableMaybe.isPresent()) {
437 final Tables table = tableMaybe.get();
438 if (table.getRoutes() instanceof Ipv4RoutesCase) {
439 final Ipv4RoutesCase routesCase = (Ipv4RoutesCase) table.getRoutes();
440 if (routesCase.getIpv4Routes() != null && routesCase.getIpv4Routes().getIpv4Route() != null) {
441 return routesCase.getIpv4Routes().getIpv4Route().size();
443 } else if (table.getRoutes() instanceof Ipv6RoutesCase) {
444 final Ipv6RoutesCase routesCase = (Ipv6RoutesCase) table.getRoutes();
445 if (routesCase.getIpv6Routes() != null && routesCase.getIpv6Routes().getIpv6Route() != null) {
446 return routesCase.getIpv6Routes().getIpv6Route().size();
450 } catch (final ReadFailedException e) {
457 public DOMTransactionChain createPeerChain(final TransactionChainListener listener) {
458 return this.domDataBroker.createTransactionChain(listener);
462 public RIBExtensionConsumerContext getRibExtensions() {
463 return this.extensions;