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(), new RibBuilder().setKey(new RibKey(ribId)).setId(ribId).setLocRib(
180 new LocRibBuilder().setTables(Collections.<Tables> emptyList()).build()).build(), true);
182 for (final BgpTableType t : localTables) {
183 final TablesKey key = new TablesKey(t.getAfi(), t.getSafi());
184 if (this.tables.create(trans, this, key) == null) {
185 LOG.debug("Did not create local table for unhandled table type {}", t);
189 Futures.addCallback(trans.submit(), new FutureCallback<Void>() {
191 public void onSuccess(final Void result) {
192 LOG.trace("Change committed successfully");
196 public void onFailure(final Throwable t) {
197 LOG.error("Failed to initiate RIB {}", getInstanceIdentifier(), t);
202 synchronized void initTables(final byte[] remoteBgpId) {
206 public synchronized void updateTables(final Peer peer, final Update message) {
207 final AdjRIBsTransactionImpl trans = new AdjRIBsTransactionImpl(this.ribOuts, this.comparator, this.chain.newWriteOnlyTransaction());
209 if (!EOR.equals(message)) {
210 final WithdrawnRoutes wr = message.getWithdrawnRoutes();
212 final AdjRIBsIn<?, ?> ari = this.tables.get(IPV4_UNICAST_TABLE);
215 * create MPUnreach for the routes to be handled in the same way as linkstate routes
217 final List<Ipv4Prefixes> prefixes = new ArrayList<>();
218 for (final Ipv4Prefix p : wr.getWithdrawnRoutes()) {
219 prefixes.add(new Ipv4PrefixesBuilder().setPrefix(p).build());
224 new MpUnreachNlriBuilder().setAfi(Ipv4AddressFamily.class).setSafi(UnicastSubsequentAddressFamily.class).setWithdrawnRoutes(
225 new WithdrawnRoutesBuilder().setDestinationType(
226 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(
227 new DestinationIpv4Builder().setIpv4Prefixes(prefixes).build()).build()).build()).build());
229 LOG.debug("Not removing objects from unhandled IPv4 Unicast");
233 final PathAttributes attrs = message.getPathAttributes();
235 final PathAttributes2 mpu = attrs.getAugmentation(PathAttributes2.class);
237 final MpUnreachNlri nlri = mpu.getMpUnreachNlri();
238 final AdjRIBsIn<?, ?> ari = this.tables.get(new TablesKey(nlri.getAfi(), nlri.getSafi()));
239 // EOR messages do not contain withdrawn routes
240 if (nlri.getWithdrawnRoutes() != null) {
242 ari.removeRoutes(trans, peer, nlri);
244 LOG.debug("Not removing objects from unhandled NLRI {}", nlri);
247 ari.markUptodate(trans, peer);
252 final Nlri ar = message.getNlri();
254 final AdjRIBsIn<?, ?> ari = this.tables.get(IPV4_UNICAST_TABLE);
257 * create MPReach for the routes to be handled in the same way as linkstate routes
259 final List<Ipv4Prefixes> prefixes = new ArrayList<>();
260 for (final Ipv4Prefix p : ar.getNlri()) {
261 prefixes.add(new Ipv4PrefixesBuilder().setPrefix(p).build());
263 final MpReachNlriBuilder b = new MpReachNlriBuilder().setAfi(Ipv4AddressFamily.class).setSafi(
264 UnicastSubsequentAddressFamily.class).setAdvertizedRoutes(
265 new AdvertizedRoutesBuilder().setDestinationType(
266 new DestinationIpv4CaseBuilder().setDestinationIpv4(
267 new DestinationIpv4Builder().setIpv4Prefixes(prefixes).build()).build()).build());
269 b.setCNextHop(attrs.getCNextHop());
272 ari.addRoutes(trans, peer, b.build(), attrs);
274 LOG.debug("Not adding objects from unhandled IPv4 Unicast");
279 final PathAttributes1 mpr = attrs.getAugmentation(PathAttributes1.class);
281 final MpReachNlri nlri = mpr.getMpReachNlri();
283 final AdjRIBsIn<?, ?> ari = this.tables.get(new TablesKey(nlri.getAfi(), nlri.getSafi()));
285 if (message.equals(ari.endOfRib())) {
286 ari.markUptodate(trans, peer);
288 ari.addRoutes(trans, peer, nlri, attrs);
291 LOG.debug("Not adding objects from unhandled NLRI {}", nlri);
296 final AdjRIBsIn<?, ?> ari = this.tables.get(IPV4_UNICAST_TABLE);
298 ari.markUptodate(trans, peer);
300 LOG.debug("End-of-RIB for IPv4 Unicast ignored");
304 Futures.addCallback(trans.commit(), new FutureCallback<Void>() {
306 public void onSuccess(final Void result) {
307 LOG.debug("RIB modification successfully committed.");
311 public void onFailure(final Throwable t) {
312 LOG.error("Failed to commit RIB modification", t);
318 public synchronized void clearTable(final Peer peer, final TablesKey key) {
319 final AdjRIBsIn<?, ?> ari = this.tables.get(key);
321 final AdjRIBsTransactionImpl trans = new AdjRIBsTransactionImpl(this.ribOuts, this.comparator, this.chain.newWriteOnlyTransaction());
322 ari.clear(trans, peer);
324 Futures.addCallback(trans.commit(), new FutureCallback<Void>() {
326 public void onSuccess(final Void result) {
327 LOG.trace("Table {} cleared successfully", key);
331 public void onFailure(final Throwable t) {
332 LOG.error("Failed to clear table {}", key, t);
339 public String toString() {
340 return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
343 protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
344 return toStringHelper;
347 @SuppressWarnings("unchecked")
348 protected <K, V extends Route> AdjRIBsIn<K, V> getTable(final TablesKey key) {
349 return (AdjRIBsIn<K, V>) this.tables.get(key);
353 public synchronized void close() throws InterruptedException, ExecutionException {
354 final WriteTransaction t = this.chain.newWriteOnlyTransaction();
355 t.delete(LogicalDatastoreType.OPERATIONAL, getInstanceIdentifier());
361 public AsNumber getLocalAs() {
366 public Ipv4Address getBgpIdentifier() {
367 return this.bgpIdentifier;
371 public Set<? extends BgpTableType> getLocalTables() {
372 return this.localTables;
376 public ReconnectStrategyFactory getTcpStrategyFactory() {
377 return this.tcpStrategyFactory;
381 public ReconnectStrategyFactory getSessionStrategyFactory() {
382 return this.sessionStrategyFactory;
386 public BGPDispatcher getDispatcher() {
387 return this.dispatcher;
391 public void initTable(final Peer bgpPeer, final TablesKey key) {
392 // FIXME: BUG-196: support graceful restart
396 public AdjRIBsOutRegistration registerRIBsOut(final Peer peer, final AdjRIBsOut aro) {
397 final AdjRIBsOutRegistration reg = new AdjRIBsOutRegistration(aro) {
399 protected void removeRegistration() {
400 RIBImpl.this.ribOuts.remove(peer, aro);
404 this.ribOuts.put(peer, aro);
405 LOG.debug("Registering this peer {} to RIB-Out {}", peer, this.ribOuts);
407 this.peers.put(peer);
408 new Thread(this.scheduler).start();
409 } catch (final InterruptedException e) {
416 public void onTransactionChainFailed(final TransactionChain<?, ?> chain, final AsyncTransaction<?, ?> transaction, final Throwable cause) {
417 LOG.error("Broken chain in RIB {} transaction {}", getInstanceIdentifier(), transaction.getIdentifier(), cause);
421 public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
422 LOG.info("RIB {} closed successfully", getInstanceIdentifier());
426 public long getRoutesCount(final TablesKey key) {
428 final Optional<Tables> tableMaybe = this.dataBroker.newReadOnlyTransaction().read(LogicalDatastoreType.OPERATIONAL,
429 getInstanceIdentifier().child(LocRib.class).child(Tables.class, key)).checkedGet();
430 if (tableMaybe.isPresent()) {
431 final Tables table = tableMaybe.get();
432 if (table.getRoutes() instanceof Ipv4RoutesCase) {
433 final Ipv4RoutesCase routesCase = (Ipv4RoutesCase) table.getRoutes();
434 if (routesCase.getIpv4Routes() != null && routesCase.getIpv4Routes().getIpv4Route() != null) {
435 return routesCase.getIpv4Routes().getIpv4Route().size();
437 } else if (table.getRoutes() instanceof Ipv6RoutesCase) {
438 final Ipv6RoutesCase routesCase = (Ipv6RoutesCase) table.getRoutes();
439 if (routesCase.getIpv6Routes() != null && routesCase.getIpv6Routes().getIpv6Route() != null) {
440 return routesCase.getIpv6Routes().getIpv6Route().size();
444 } catch (final ReadFailedException e) {
451 public DOMTransactionChain createPeerChain(final TransactionChainListener listener) {
452 return domDataBroker.createTransactionChain(listener);
456 public RIBExtensionConsumerContext getRibExtensions() {