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.yang.binding.InstanceIdentifier;
84 import org.slf4j.Logger;
85 import org.slf4j.LoggerFactory;
88 public final class RIBImpl extends DefaultRibReference implements AutoCloseable, RIB, TransactionChainListener {
89 private static final Logger LOG = LoggerFactory.getLogger(RIBImpl.class);
90 private static final Update EOR = new UpdateBuilder().build();
91 private static final TablesKey IPV4_UNICAST_TABLE = new TablesKey(Ipv4AddressFamily.class, UnicastSubsequentAddressFamily.class);
94 * FIXME: performance: this needs to be turned into a Peer->offset map.
95 * The offset is used to locate a the per-peer state entry in the
98 * For the first release, that map is updated whenever configuration
99 * changes and remains constant on peer flaps. On re-configuration
100 * a resize task is scheduled, so large tables may take some time
101 * before they continue reacting to updates.
103 * For subsequent releases, if we make the reformat process concurrent,
104 * we can trigger reformats when Graceful Restart Time expires for a
107 private final ConcurrentMap<Peer, AdjRIBsOut> ribOuts = new ConcurrentHashMap<>();
108 private final ReconnectStrategyFactory tcpStrategyFactory;
109 private final ReconnectStrategyFactory sessionStrategyFactory;
112 * BGP Best Path selection comparator for ingress best path selection.
114 private final BGPObjectComparator comparator;
115 private final BGPDispatcher dispatcher;
116 private final BindingTransactionChain chain;
117 private final AsNumber localAs;
118 private final Ipv4Address bgpIdentifier;
119 private final Set<BgpTableType> localTables;
120 private final RIBTables tables;
121 private final BlockingQueue<Peer> peers;
122 private final DataBroker dataBroker;
123 private final DOMDataBroker domDataBroker;
124 private final RIBExtensionConsumerContext extensions;
126 private final Runnable scheduler = new Runnable() {
130 final Peer peer = RIBImpl.this.peers.take();
131 LOG.debug("Advertizing loc-rib to new peer {}.", peer);
132 for (final BgpTableType key : RIBImpl.this.localTables) {
134 synchronized (RIBImpl.this) {
135 final AdjRIBsTransactionImpl trans = new AdjRIBsTransactionImpl(RIBImpl.this.ribOuts, RIBImpl.this.comparator, RIBImpl.this.chain.newWriteOnlyTransaction());
136 final AbstractAdjRIBs<?, ?, ?> adj = (AbstractAdjRIBs<?, ?, ?>) RIBImpl.this.tables.get(new TablesKey(key.getAfi(), key.getSafi()));
137 adj.addAllEntries(trans);
138 Futures.addCallback(trans.commit(), new FutureCallback<Void>() {
140 public void onSuccess(final Void result) {
141 LOG.trace("Advertizing {} to peer {} committed successfully", key.getAfi(), peer);
144 public void onFailure(final Throwable t) {
145 LOG.error("Failed to update peer {} with RIB {}", peer, t);
150 } catch (final InterruptedException e) {
151 LOG.info("Scheduler thread was interrupted.", e);
156 public RIBImpl(final RibId ribId, final AsNumber localAs, final Ipv4Address localBgpId, final RIBExtensionConsumerContext extensions,
157 final BGPDispatcher dispatcher, final ReconnectStrategyFactory tcpStrategyFactory,
158 final ReconnectStrategyFactory sessionStrategyFactory, final DataBroker dps, final DOMDataBroker domDataBroker, final List<BgpTableType> localTables) {
159 super(InstanceIdentifier.create(BgpRib.class).child(Rib.class, new RibKey(Preconditions.checkNotNull(ribId))));
160 this.chain = dps.createTransactionChain(this);
161 this.localAs = Preconditions.checkNotNull(localAs);
162 this.comparator = new BGPObjectComparator(localAs);
163 this.bgpIdentifier = Preconditions.checkNotNull(localBgpId);
164 this.dispatcher = Preconditions.checkNotNull(dispatcher);
165 this.sessionStrategyFactory = Preconditions.checkNotNull(sessionStrategyFactory);
166 this.tcpStrategyFactory = Preconditions.checkNotNull(tcpStrategyFactory);
167 this.localTables = ImmutableSet.copyOf(localTables);
168 this.tables = new RIBTables(extensions);
169 this.peers = new LinkedBlockingQueue<>();
170 this.dataBroker = dps;
171 this.domDataBroker = Preconditions.checkNotNull(domDataBroker);
172 this.extensions = Preconditions.checkNotNull(extensions);
174 LOG.debug("Instantiating RIB table {} at {}", ribId, getInstanceIdentifier());
176 final WriteTransaction trans = this.chain.newWriteOnlyTransaction();
178 // put empty BgpRib if not exists
179 trans.put(LogicalDatastoreType.OPERATIONAL, getInstanceIdentifier(),
180 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(
181 new LocRibBuilder().setTables(Collections.<Tables> emptyList()).build()).build(), true);
183 for (final BgpTableType t : localTables) {
184 final TablesKey key = new TablesKey(t.getAfi(), t.getSafi());
185 if (this.tables.create(trans, this, key) == null) {
186 LOG.debug("Did not create local table for unhandled table type {}", t);
190 Futures.addCallback(trans.submit(), new FutureCallback<Void>() {
192 public void onSuccess(final Void result) {
193 LOG.trace("Change committed successfully");
197 public void onFailure(final Throwable t) {
198 LOG.error("Failed to initiate RIB {}", getInstanceIdentifier(), t);
203 synchronized void initTables(final byte[] remoteBgpId) {
207 public synchronized void updateTables(final Peer peer, final Update message) {
208 final AdjRIBsTransactionImpl trans = new AdjRIBsTransactionImpl(this.ribOuts, this.comparator, this.chain.newWriteOnlyTransaction());
210 if (!EOR.equals(message)) {
211 final WithdrawnRoutes wr = message.getWithdrawnRoutes();
213 final AdjRIBsIn<?, ?> ari = this.tables.get(IPV4_UNICAST_TABLE);
216 * create MPUnreach for the routes to be handled in the same way as linkstate routes
218 final List<Ipv4Prefixes> prefixes = new ArrayList<>();
219 for (final Ipv4Prefix p : wr.getWithdrawnRoutes()) {
220 prefixes.add(new Ipv4PrefixesBuilder().setPrefix(p).build());
225 new MpUnreachNlriBuilder().setAfi(Ipv4AddressFamily.class).setSafi(UnicastSubsequentAddressFamily.class).setWithdrawnRoutes(
226 new WithdrawnRoutesBuilder().setDestinationType(
227 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(
228 new DestinationIpv4Builder().setIpv4Prefixes(prefixes).build()).build()).build()).build());
230 LOG.debug("Not removing objects from unhandled IPv4 Unicast");
234 final PathAttributes attrs = message.getPathAttributes();
236 final PathAttributes2 mpu = attrs.getAugmentation(PathAttributes2.class);
238 final MpUnreachNlri nlri = mpu.getMpUnreachNlri();
239 final AdjRIBsIn<?, ?> ari = this.tables.get(new TablesKey(nlri.getAfi(), nlri.getSafi()));
240 // EOR messages do not contain withdrawn routes
241 if (nlri.getWithdrawnRoutes() != null) {
243 ari.removeRoutes(trans, peer, nlri);
245 LOG.debug("Not removing objects from unhandled NLRI {}", nlri);
248 ari.markUptodate(trans, peer);
253 final Nlri ar = message.getNlri();
255 final AdjRIBsIn<?, ?> ari = this.tables.get(IPV4_UNICAST_TABLE);
258 * create MPReach for the routes to be handled in the same way as linkstate routes
260 final List<Ipv4Prefixes> prefixes = new ArrayList<>();
261 for (final Ipv4Prefix p : ar.getNlri()) {
262 prefixes.add(new Ipv4PrefixesBuilder().setPrefix(p).build());
264 final MpReachNlriBuilder b = new MpReachNlriBuilder().setAfi(Ipv4AddressFamily.class).setSafi(
265 UnicastSubsequentAddressFamily.class).setAdvertizedRoutes(
266 new AdvertizedRoutesBuilder().setDestinationType(
267 new DestinationIpv4CaseBuilder().setDestinationIpv4(
268 new DestinationIpv4Builder().setIpv4Prefixes(prefixes).build()).build()).build());
270 b.setCNextHop(attrs.getCNextHop());
273 ari.addRoutes(trans, peer, b.build(), attrs);
275 LOG.debug("Not adding objects from unhandled IPv4 Unicast");
280 final PathAttributes1 mpr = attrs.getAugmentation(PathAttributes1.class);
282 final MpReachNlri nlri = mpr.getMpReachNlri();
284 final AdjRIBsIn<?, ?> ari = this.tables.get(new TablesKey(nlri.getAfi(), nlri.getSafi()));
286 if (message.equals(ari.endOfRib())) {
287 ari.markUptodate(trans, peer);
289 ari.addRoutes(trans, peer, nlri, attrs);
292 LOG.debug("Not adding objects from unhandled NLRI {}", nlri);
297 final AdjRIBsIn<?, ?> ari = this.tables.get(IPV4_UNICAST_TABLE);
299 ari.markUptodate(trans, peer);
301 LOG.debug("End-of-RIB for IPv4 Unicast ignored");
305 Futures.addCallback(trans.commit(), new FutureCallback<Void>() {
307 public void onSuccess(final Void result) {
308 LOG.debug("RIB modification successfully committed.");
312 public void onFailure(final Throwable t) {
313 LOG.error("Failed to commit RIB modification", t);
319 public synchronized void clearTable(final Peer peer, final TablesKey key) {
320 final AdjRIBsIn<?, ?> ari = this.tables.get(key);
322 final AdjRIBsTransactionImpl trans = new AdjRIBsTransactionImpl(this.ribOuts, this.comparator, this.chain.newWriteOnlyTransaction());
323 ari.clear(trans, peer);
325 Futures.addCallback(trans.commit(), new FutureCallback<Void>() {
327 public void onSuccess(final Void result) {
328 LOG.trace("Table {} cleared successfully", key);
332 public void onFailure(final Throwable t) {
333 LOG.error("Failed to clear table {}", key, t);
340 public String toString() {
341 return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
344 protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
345 return toStringHelper;
348 @SuppressWarnings("unchecked")
349 protected <K, V extends Route> AdjRIBsIn<K, V> getTable(final TablesKey key) {
350 return (AdjRIBsIn<K, V>) this.tables.get(key);
354 public synchronized void close() throws InterruptedException, ExecutionException {
355 final WriteTransaction t = this.chain.newWriteOnlyTransaction();
356 t.delete(LogicalDatastoreType.OPERATIONAL, getInstanceIdentifier());
362 public AsNumber getLocalAs() {
367 public Ipv4Address getBgpIdentifier() {
368 return this.bgpIdentifier;
372 public Set<? extends BgpTableType> getLocalTables() {
373 return this.localTables;
377 public ReconnectStrategyFactory getTcpStrategyFactory() {
378 return this.tcpStrategyFactory;
382 public ReconnectStrategyFactory getSessionStrategyFactory() {
383 return this.sessionStrategyFactory;
387 public BGPDispatcher getDispatcher() {
388 return this.dispatcher;
392 public void initTable(final Peer bgpPeer, final TablesKey key) {
393 // FIXME: BUG-196: support graceful restart
397 public AdjRIBsOutRegistration registerRIBsOut(final Peer peer, final AdjRIBsOut aro) {
398 final AdjRIBsOutRegistration reg = new AdjRIBsOutRegistration(aro) {
400 protected void removeRegistration() {
401 RIBImpl.this.ribOuts.remove(peer, aro);
405 this.ribOuts.put(peer, aro);
406 LOG.debug("Registering this peer {} to RIB-Out {}", peer, this.ribOuts);
408 this.peers.put(peer);
409 new Thread(this.scheduler).start();
410 } catch (final InterruptedException e) {
417 public void onTransactionChainFailed(final TransactionChain<?, ?> chain, final AsyncTransaction<?, ?> transaction, final Throwable cause) {
418 LOG.error("Broken chain in RIB {} transaction {}", getInstanceIdentifier(), transaction.getIdentifier(), cause);
422 public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
423 LOG.info("RIB {} closed successfully", getInstanceIdentifier());
427 public long getRoutesCount(final TablesKey key) {
429 final Optional<Tables> tableMaybe = this.dataBroker.newReadOnlyTransaction().read(LogicalDatastoreType.OPERATIONAL,
430 getInstanceIdentifier().child(LocRib.class).child(Tables.class, key)).checkedGet();
431 if (tableMaybe.isPresent()) {
432 final Tables table = tableMaybe.get();
433 if (table.getRoutes() instanceof Ipv4RoutesCase) {
434 final Ipv4RoutesCase routesCase = (Ipv4RoutesCase) table.getRoutes();
435 if (routesCase.getIpv4Routes() != null && routesCase.getIpv4Routes().getIpv4Route() != null) {
436 return routesCase.getIpv4Routes().getIpv4Route().size();
438 } else if (table.getRoutes() instanceof Ipv6RoutesCase) {
439 final Ipv6RoutesCase routesCase = (Ipv6RoutesCase) table.getRoutes();
440 if (routesCase.getIpv6Routes() != null && routesCase.getIpv6Routes().getIpv6Route() != null) {
441 return routesCase.getIpv6Routes().getIpv6Route().size();
445 } catch (final ReadFailedException e) {
452 public DOMTransactionChain createPeerChain(final TransactionChainListener listener) {
453 return this.domDataBroker.createTransactionChain(listener);
457 public RIBExtensionConsumerContext getRibExtensions() {
458 return this.extensions;