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;
125 private final Runnable scheduler = new Runnable() {
129 final Peer peer = RIBImpl.this.peers.take();
130 LOG.debug("Advertizing loc-rib to new peer {}.", peer);
131 for (final BgpTableType key : RIBImpl.this.localTables) {
133 synchronized (RIBImpl.this) {
134 final AdjRIBsTransactionImpl trans = new AdjRIBsTransactionImpl(RIBImpl.this.ribOuts, RIBImpl.this.comparator, RIBImpl.this.chain.newWriteOnlyTransaction());
135 final AbstractAdjRIBs<?, ?, ?> adj = (AbstractAdjRIBs<?, ?, ?>) RIBImpl.this.tables.get(new TablesKey(key.getAfi(), key.getSafi()));
136 adj.addAllEntries(trans);
137 Futures.addCallback(trans.commit(), new FutureCallback<Void>() {
139 public void onSuccess(final Void result) {
140 LOG.trace("Advertizing {} to peer {} committed successfully", key.getAfi(), peer);
143 public void onFailure(final Throwable t) {
144 LOG.error("Failed to update peer {} with RIB {}", peer, t);
149 } catch (final InterruptedException e) {
150 LOG.info("Scheduler thread was interrupted.", e);
155 public RIBImpl(final RibId ribId, final AsNumber localAs, final Ipv4Address localBgpId, final RIBExtensionConsumerContext extensions,
156 final BGPDispatcher dispatcher, final ReconnectStrategyFactory tcpStrategyFactory,
157 final ReconnectStrategyFactory sessionStrategyFactory, final DataBroker dps, final DOMDataBroker domDataBroker, final List<BgpTableType> localTables) {
158 super(InstanceIdentifier.create(BgpRib.class).child(Rib.class, new RibKey(Preconditions.checkNotNull(ribId))));
159 this.chain = dps.createTransactionChain(this);
160 this.localAs = Preconditions.checkNotNull(localAs);
161 this.comparator = new BGPObjectComparator(localAs);
162 this.bgpIdentifier = Preconditions.checkNotNull(localBgpId);
163 this.dispatcher = Preconditions.checkNotNull(dispatcher);
164 this.sessionStrategyFactory = Preconditions.checkNotNull(sessionStrategyFactory);
165 this.tcpStrategyFactory = Preconditions.checkNotNull(tcpStrategyFactory);
166 this.localTables = ImmutableSet.copyOf(localTables);
167 this.tables = new RIBTables(extensions);
168 this.peers = new LinkedBlockingQueue<>();
169 this.dataBroker = dps;
170 this.domDataBroker = Preconditions.checkNotNull(domDataBroker);
172 LOG.debug("Instantiating RIB table {} at {}", ribId, getInstanceIdentifier());
174 final WriteTransaction trans = this.chain.newWriteOnlyTransaction();
176 // put empty BgpRib if not exists
177 trans.put(LogicalDatastoreType.OPERATIONAL, getInstanceIdentifier(), new RibBuilder().setKey(new RibKey(ribId)).setId(ribId).setLocRib(
178 new LocRibBuilder().setTables(Collections.<Tables> emptyList()).build()).build(), true);
180 for (final BgpTableType t : localTables) {
181 final TablesKey key = new TablesKey(t.getAfi(), t.getSafi());
182 if (this.tables.create(trans, this, key) == null) {
183 LOG.debug("Did not create local table for unhandled table type {}", t);
187 Futures.addCallback(trans.submit(), new FutureCallback<Void>() {
189 public void onSuccess(final Void result) {
190 LOG.trace("Change committed successfully");
194 public void onFailure(final Throwable t) {
195 LOG.error("Failed to initiate RIB {}", getInstanceIdentifier(), t);
200 synchronized void initTables(final byte[] remoteBgpId) {
204 public synchronized void updateTables(final Peer peer, final Update message) {
205 final AdjRIBsTransactionImpl trans = new AdjRIBsTransactionImpl(this.ribOuts, this.comparator, this.chain.newWriteOnlyTransaction());
207 if (!EOR.equals(message)) {
208 final WithdrawnRoutes wr = message.getWithdrawnRoutes();
210 final AdjRIBsIn<?, ?> ari = this.tables.get(IPV4_UNICAST_TABLE);
213 * create MPUnreach for the routes to be handled in the same way as linkstate routes
215 final List<Ipv4Prefixes> prefixes = new ArrayList<>();
216 for (final Ipv4Prefix p : wr.getWithdrawnRoutes()) {
217 prefixes.add(new Ipv4PrefixesBuilder().setPrefix(p).build());
222 new MpUnreachNlriBuilder().setAfi(Ipv4AddressFamily.class).setSafi(UnicastSubsequentAddressFamily.class).setWithdrawnRoutes(
223 new WithdrawnRoutesBuilder().setDestinationType(
224 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(
225 new DestinationIpv4Builder().setIpv4Prefixes(prefixes).build()).build()).build()).build());
227 LOG.debug("Not removing objects from unhandled IPv4 Unicast");
231 final PathAttributes attrs = message.getPathAttributes();
233 final PathAttributes2 mpu = attrs.getAugmentation(PathAttributes2.class);
235 final MpUnreachNlri nlri = mpu.getMpUnreachNlri();
236 final AdjRIBsIn<?, ?> ari = this.tables.get(new TablesKey(nlri.getAfi(), nlri.getSafi()));
237 // EOR messages do not contain withdrawn routes
238 if (nlri.getWithdrawnRoutes() != null) {
240 ari.removeRoutes(trans, peer, nlri);
242 LOG.debug("Not removing objects from unhandled NLRI {}", nlri);
245 ari.markUptodate(trans, peer);
250 final Nlri ar = message.getNlri();
252 final AdjRIBsIn<?, ?> ari = this.tables.get(IPV4_UNICAST_TABLE);
255 * create MPReach for the routes to be handled in the same way as linkstate routes
257 final List<Ipv4Prefixes> prefixes = new ArrayList<>();
258 for (final Ipv4Prefix p : ar.getNlri()) {
259 prefixes.add(new Ipv4PrefixesBuilder().setPrefix(p).build());
261 final MpReachNlriBuilder b = new MpReachNlriBuilder().setAfi(Ipv4AddressFamily.class).setSafi(
262 UnicastSubsequentAddressFamily.class).setAdvertizedRoutes(
263 new AdvertizedRoutesBuilder().setDestinationType(
264 new DestinationIpv4CaseBuilder().setDestinationIpv4(
265 new DestinationIpv4Builder().setIpv4Prefixes(prefixes).build()).build()).build());
267 b.setCNextHop(attrs.getCNextHop());
270 ari.addRoutes(trans, peer, b.build(), attrs);
272 LOG.debug("Not adding objects from unhandled IPv4 Unicast");
277 final PathAttributes1 mpr = attrs.getAugmentation(PathAttributes1.class);
279 final MpReachNlri nlri = mpr.getMpReachNlri();
281 final AdjRIBsIn<?, ?> ari = this.tables.get(new TablesKey(nlri.getAfi(), nlri.getSafi()));
283 if (message.equals(ari.endOfRib())) {
284 ari.markUptodate(trans, peer);
286 ari.addRoutes(trans, peer, nlri, attrs);
289 LOG.debug("Not adding objects from unhandled NLRI {}", nlri);
294 final AdjRIBsIn<?, ?> ari = this.tables.get(IPV4_UNICAST_TABLE);
296 ari.markUptodate(trans, peer);
298 LOG.debug("End-of-RIB for IPv4 Unicast ignored");
302 Futures.addCallback(trans.commit(), new FutureCallback<Void>() {
304 public void onSuccess(final Void result) {
305 LOG.debug("RIB modification successfully committed.");
309 public void onFailure(final Throwable t) {
310 LOG.error("Failed to commit RIB modification", t);
316 public synchronized void clearTable(final Peer peer, final TablesKey key) {
317 final AdjRIBsIn<?, ?> ari = this.tables.get(key);
319 final AdjRIBsTransactionImpl trans = new AdjRIBsTransactionImpl(this.ribOuts, this.comparator, this.chain.newWriteOnlyTransaction());
320 ari.clear(trans, peer);
322 Futures.addCallback(trans.commit(), new FutureCallback<Void>() {
324 public void onSuccess(final Void result) {
325 LOG.trace("Table {} cleared successfully", key);
329 public void onFailure(final Throwable t) {
330 LOG.error("Failed to clear table {}", key, t);
337 public String toString() {
338 return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
341 protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
342 return toStringHelper;
345 @SuppressWarnings("unchecked")
346 protected <K, V extends Route> AdjRIBsIn<K, V> getTable(final TablesKey key) {
347 return (AdjRIBsIn<K, V>) this.tables.get(key);
351 public synchronized void close() throws InterruptedException, ExecutionException {
352 final WriteTransaction t = this.chain.newWriteOnlyTransaction();
353 t.delete(LogicalDatastoreType.OPERATIONAL, getInstanceIdentifier());
359 public AsNumber getLocalAs() {
364 public Ipv4Address getBgpIdentifier() {
365 return this.bgpIdentifier;
369 public Set<? extends BgpTableType> getLocalTables() {
370 return this.localTables;
374 public ReconnectStrategyFactory getTcpStrategyFactory() {
375 return this.tcpStrategyFactory;
379 public ReconnectStrategyFactory getSessionStrategyFactory() {
380 return this.sessionStrategyFactory;
384 public BGPDispatcher getDispatcher() {
385 return this.dispatcher;
389 public void initTable(final Peer bgpPeer, final TablesKey key) {
390 // FIXME: BUG-196: support graceful restart
394 public AdjRIBsOutRegistration registerRIBsOut(final Peer peer, final AdjRIBsOut aro) {
395 final AdjRIBsOutRegistration reg = new AdjRIBsOutRegistration(aro) {
397 protected void removeRegistration() {
398 RIBImpl.this.ribOuts.remove(peer, aro);
402 this.ribOuts.put(peer, aro);
403 LOG.debug("Registering this peer {} to RIB-Out {}", peer, this.ribOuts);
405 this.peers.put(peer);
406 new Thread(this.scheduler).start();
407 } catch (final InterruptedException e) {
414 public void onTransactionChainFailed(final TransactionChain<?, ?> chain, final AsyncTransaction<?, ?> transaction, final Throwable cause) {
415 LOG.error("Broken chain in RIB {} transaction {}", getInstanceIdentifier(), transaction.getIdentifier(), cause);
419 public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
420 LOG.info("RIB {} closed successfully", getInstanceIdentifier());
424 public long getRoutesCount(final TablesKey key) {
426 final Optional<Tables> tableMaybe = this.dataBroker.newReadOnlyTransaction().read(LogicalDatastoreType.OPERATIONAL,
427 getInstanceIdentifier().child(LocRib.class).child(Tables.class, key)).checkedGet();
428 if (tableMaybe.isPresent()) {
429 final Tables table = tableMaybe.get();
430 if (table.getRoutes() instanceof Ipv4RoutesCase) {
431 final Ipv4RoutesCase routesCase = (Ipv4RoutesCase) table.getRoutes();
432 if (routesCase.getIpv4Routes() != null && routesCase.getIpv4Routes().getIpv4Route() != null) {
433 return routesCase.getIpv4Routes().getIpv4Route().size();
435 } else if (table.getRoutes() instanceof Ipv6RoutesCase) {
436 final Ipv6RoutesCase routesCase = (Ipv6RoutesCase) table.getRoutes();
437 if (routesCase.getIpv6Routes() != null && routesCase.getIpv6Routes().getIpv6Route() != null) {
438 return routesCase.getIpv6Routes().getIpv6Route().size();
442 } catch (final ReadFailedException e) {
449 public DOMTransactionChain createPeerChain(final TransactionChainListener listener) {
450 return domDataBroker.createTransactionChain(listener);