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 Set<BgpTableType> localTables;
121 private final RIBTables tables;
122 private final BlockingQueue<Peer> peers;
123 private final DataBroker dataBroker;
124 private final DOMDataBroker domDataBroker;
125 private final RIBExtensionConsumerContext extensions;
126 private final BindingCodecTreeFactory codecFactory;
128 private final Runnable scheduler = new Runnable() {
132 final Peer peer = RIBImpl.this.peers.take();
133 LOG.debug("Advertizing loc-rib to new peer {}.", peer);
134 for (final BgpTableType key : RIBImpl.this.localTables) {
136 synchronized (RIBImpl.this) {
137 final AdjRIBsTransactionImpl trans = new AdjRIBsTransactionImpl(RIBImpl.this.ribOuts, RIBImpl.this.comparator, RIBImpl.this.chain.newWriteOnlyTransaction());
138 final AbstractAdjRIBs<?, ?, ?> adj = (AbstractAdjRIBs<?, ?, ?>) RIBImpl.this.tables.get(new TablesKey(key.getAfi(), key.getSafi()));
139 adj.addAllEntries(trans);
140 Futures.addCallback(trans.commit(), new FutureCallback<Void>() {
142 public void onSuccess(final Void result) {
143 LOG.trace("Advertizing {} to peer {} committed successfully", key.getAfi(), peer);
146 public void onFailure(final Throwable t) {
147 LOG.error("Failed to update peer {} with RIB {}", peer, t);
152 } catch (final InterruptedException e) {
153 LOG.info("Scheduler thread was interrupted.", e);
158 public RIBImpl(final RibId ribId, final AsNumber localAs, final Ipv4Address localBgpId, final RIBExtensionConsumerContext extensions,
159 final BGPDispatcher dispatcher, final ReconnectStrategyFactory tcpStrategyFactory, final BindingCodecTreeFactory codecFactory,
160 final ReconnectStrategyFactory sessionStrategyFactory, final DataBroker dps, final DOMDataBroker domDataBroker, final List<BgpTableType> localTables) {
161 super(InstanceIdentifier.create(BgpRib.class).child(Rib.class, new RibKey(Preconditions.checkNotNull(ribId))));
162 this.chain = dps.createTransactionChain(this);
163 this.localAs = Preconditions.checkNotNull(localAs);
164 this.comparator = new BGPObjectComparator(localAs);
165 this.bgpIdentifier = Preconditions.checkNotNull(localBgpId);
166 this.dispatcher = Preconditions.checkNotNull(dispatcher);
167 this.sessionStrategyFactory = Preconditions.checkNotNull(sessionStrategyFactory);
168 this.tcpStrategyFactory = Preconditions.checkNotNull(tcpStrategyFactory);
169 this.localTables = ImmutableSet.copyOf(localTables);
170 this.tables = new RIBTables(extensions);
171 this.peers = new LinkedBlockingQueue<>();
172 this.dataBroker = dps;
173 this.domDataBroker = Preconditions.checkNotNull(domDataBroker);
174 this.extensions = Preconditions.checkNotNull(extensions);
175 this.codecFactory = codecFactory;
177 LOG.debug("Instantiating RIB table {} at {}", ribId, getInstanceIdentifier());
179 final WriteTransaction trans = this.chain.newWriteOnlyTransaction();
181 // put empty BgpRib if not exists
182 trans.put(LogicalDatastoreType.OPERATIONAL, getInstanceIdentifier(),
183 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(
184 new LocRibBuilder().setTables(Collections.<Tables> emptyList()).build()).build(), true);
186 for (final BgpTableType t : localTables) {
187 final TablesKey key = new TablesKey(t.getAfi(), t.getSafi());
188 if (this.tables.create(trans, this, key) == null) {
189 LOG.debug("Did not create local table for unhandled table type {}", t);
193 Futures.addCallback(trans.submit(), new FutureCallback<Void>() {
195 public void onSuccess(final Void result) {
196 LOG.trace("Change committed successfully");
200 public void onFailure(final Throwable t) {
201 LOG.error("Failed to initiate RIB {}", getInstanceIdentifier(), t);
206 synchronized void initTables(final byte[] remoteBgpId) {
210 public synchronized void updateTables(final Peer peer, final Update message) {
211 final AdjRIBsTransactionImpl trans = new AdjRIBsTransactionImpl(this.ribOuts, this.comparator, this.chain.newWriteOnlyTransaction());
213 if (!EOR.equals(message)) {
214 final WithdrawnRoutes wr = message.getWithdrawnRoutes();
216 final AdjRIBsIn<?, ?> ari = this.tables.get(IPV4_UNICAST_TABLE);
219 * create MPUnreach for the routes to be handled in the same way as linkstate routes
221 final List<Ipv4Prefixes> prefixes = new ArrayList<>();
222 for (final Ipv4Prefix p : wr.getWithdrawnRoutes()) {
223 prefixes.add(new Ipv4PrefixesBuilder().setPrefix(p).build());
228 new MpUnreachNlriBuilder().setAfi(Ipv4AddressFamily.class).setSafi(UnicastSubsequentAddressFamily.class).setWithdrawnRoutes(
229 new WithdrawnRoutesBuilder().setDestinationType(
230 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(
231 new DestinationIpv4Builder().setIpv4Prefixes(prefixes).build()).build()).build()).build());
233 LOG.debug("Not removing objects from unhandled IPv4 Unicast");
237 final PathAttributes attrs = message.getPathAttributes();
239 final PathAttributes2 mpu = attrs.getAugmentation(PathAttributes2.class);
241 final MpUnreachNlri nlri = mpu.getMpUnreachNlri();
242 final AdjRIBsIn<?, ?> ari = this.tables.get(new TablesKey(nlri.getAfi(), nlri.getSafi()));
243 // EOR messages do not contain withdrawn routes
244 if (nlri.getWithdrawnRoutes() != null) {
246 ari.removeRoutes(trans, peer, nlri);
248 LOG.debug("Not removing objects from unhandled NLRI {}", nlri);
251 ari.markUptodate(trans, peer);
256 final Nlri ar = message.getNlri();
258 final AdjRIBsIn<?, ?> ari = this.tables.get(IPV4_UNICAST_TABLE);
261 * create MPReach for the routes to be handled in the same way as linkstate routes
263 final List<Ipv4Prefixes> prefixes = new ArrayList<>();
264 for (final Ipv4Prefix p : ar.getNlri()) {
265 prefixes.add(new Ipv4PrefixesBuilder().setPrefix(p).build());
267 final MpReachNlriBuilder b = new MpReachNlriBuilder().setAfi(Ipv4AddressFamily.class).setSafi(
268 UnicastSubsequentAddressFamily.class).setAdvertizedRoutes(
269 new AdvertizedRoutesBuilder().setDestinationType(
270 new DestinationIpv4CaseBuilder().setDestinationIpv4(
271 new DestinationIpv4Builder().setIpv4Prefixes(prefixes).build()).build()).build());
273 b.setCNextHop(attrs.getCNextHop());
276 ari.addRoutes(trans, peer, b.build(), attrs);
278 LOG.debug("Not adding objects from unhandled IPv4 Unicast");
283 final PathAttributes1 mpr = attrs.getAugmentation(PathAttributes1.class);
285 final MpReachNlri nlri = mpr.getMpReachNlri();
287 final AdjRIBsIn<?, ?> ari = this.tables.get(new TablesKey(nlri.getAfi(), nlri.getSafi()));
289 if (message.equals(ari.endOfRib())) {
290 ari.markUptodate(trans, peer);
292 ari.addRoutes(trans, peer, nlri, attrs);
295 LOG.debug("Not adding objects from unhandled NLRI {}", nlri);
300 final AdjRIBsIn<?, ?> ari = this.tables.get(IPV4_UNICAST_TABLE);
302 ari.markUptodate(trans, peer);
304 LOG.debug("End-of-RIB for IPv4 Unicast ignored");
308 Futures.addCallback(trans.commit(), new FutureCallback<Void>() {
310 public void onSuccess(final Void result) {
311 LOG.debug("RIB modification successfully committed.");
315 public void onFailure(final Throwable t) {
316 LOG.error("Failed to commit RIB modification", t);
322 public synchronized void clearTable(final Peer peer, final TablesKey key) {
323 final AdjRIBsIn<?, ?> ari = this.tables.get(key);
325 final AdjRIBsTransactionImpl trans = new AdjRIBsTransactionImpl(this.ribOuts, this.comparator, this.chain.newWriteOnlyTransaction());
326 ari.clear(trans, peer);
328 Futures.addCallback(trans.commit(), new FutureCallback<Void>() {
330 public void onSuccess(final Void result) {
331 LOG.trace("Table {} cleared successfully", key);
335 public void onFailure(final Throwable t) {
336 LOG.error("Failed to clear table {}", key, t);
343 public String toString() {
344 return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
347 protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
348 return toStringHelper;
351 @SuppressWarnings("unchecked")
352 protected <K, V extends Route> AdjRIBsIn<K, V> getTable(final TablesKey key) {
353 return (AdjRIBsIn<K, V>) this.tables.get(key);
357 public synchronized void close() throws InterruptedException, ExecutionException {
358 final WriteTransaction t = this.chain.newWriteOnlyTransaction();
359 t.delete(LogicalDatastoreType.OPERATIONAL, getInstanceIdentifier());
365 public AsNumber getLocalAs() {
370 public Ipv4Address getBgpIdentifier() {
371 return this.bgpIdentifier;
375 public Set<? extends BgpTableType> getLocalTables() {
376 return this.localTables;
380 public ReconnectStrategyFactory getTcpStrategyFactory() {
381 return this.tcpStrategyFactory;
385 public ReconnectStrategyFactory getSessionStrategyFactory() {
386 return this.sessionStrategyFactory;
390 public BGPDispatcher getDispatcher() {
391 return this.dispatcher;
395 public void initTable(final Peer bgpPeer, final TablesKey key) {
396 // FIXME: BUG-196: support graceful restart
400 public AdjRIBsOutRegistration registerRIBsOut(final Peer peer, final AdjRIBsOut aro) {
401 final AdjRIBsOutRegistration reg = new AdjRIBsOutRegistration(aro) {
403 protected void removeRegistration() {
404 RIBImpl.this.ribOuts.remove(peer, aro);
408 this.ribOuts.put(peer, aro);
409 LOG.debug("Registering this peer {} to RIB-Out {}", peer, this.ribOuts);
411 this.peers.put(peer);
412 new Thread(this.scheduler).start();
413 } catch (final InterruptedException e) {
420 public void onTransactionChainFailed(final TransactionChain<?, ?> chain, final AsyncTransaction<?, ?> transaction, final Throwable cause) {
421 LOG.error("Broken chain in RIB {} transaction {}", getInstanceIdentifier(), transaction.getIdentifier(), cause);
425 public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
426 LOG.info("RIB {} closed successfully", getInstanceIdentifier());
430 public long getRoutesCount(final TablesKey key) {
432 final Optional<Tables> tableMaybe = this.dataBroker.newReadOnlyTransaction().read(LogicalDatastoreType.OPERATIONAL,
433 getInstanceIdentifier().child(LocRib.class).child(Tables.class, key)).checkedGet();
434 if (tableMaybe.isPresent()) {
435 final Tables table = tableMaybe.get();
436 if (table.getRoutes() instanceof Ipv4RoutesCase) {
437 final Ipv4RoutesCase routesCase = (Ipv4RoutesCase) table.getRoutes();
438 if (routesCase.getIpv4Routes() != null && routesCase.getIpv4Routes().getIpv4Route() != null) {
439 return routesCase.getIpv4Routes().getIpv4Route().size();
441 } else if (table.getRoutes() instanceof Ipv6RoutesCase) {
442 final Ipv6RoutesCase routesCase = (Ipv6RoutesCase) table.getRoutes();
443 if (routesCase.getIpv6Routes() != null && routesCase.getIpv6Routes().getIpv6Route() != null) {
444 return routesCase.getIpv6Routes().getIpv6Route().size();
448 } catch (final ReadFailedException e) {
455 public DOMTransactionChain createPeerChain(final TransactionChainListener listener) {
456 return this.domDataBroker.createTransactionChain(listener);
460 public RIBExtensionConsumerContext getRibExtensions() {
461 return this.extensions;