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.protocol.bgp.rib.DefaultRibReference;
36 import org.opendaylight.protocol.bgp.rib.impl.spi.AdjRIBsOut;
37 import org.opendaylight.protocol.bgp.rib.impl.spi.AdjRIBsOutRegistration;
38 import org.opendaylight.protocol.bgp.rib.impl.spi.BGPDispatcher;
39 import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
40 import org.opendaylight.protocol.bgp.rib.spi.AbstractAdjRIBs;
41 import org.opendaylight.protocol.bgp.rib.spi.AdjRIBsIn;
42 import org.opendaylight.protocol.bgp.rib.spi.BGPObjectComparator;
43 import org.opendaylight.protocol.bgp.rib.spi.Peer;
44 import org.opendaylight.protocol.bgp.rib.spi.RIBExtensionConsumerContext;
45 import org.opendaylight.protocol.framework.ReconnectStrategyFactory;
46 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.AsNumber;
47 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
48 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev150305.bgp.rib.rib.loc.rib.tables.routes.Ipv4RoutesCase;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev150305.bgp.rib.rib.loc.rib.tables.routes.Ipv6RoutesCase;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev150305.ipv4.prefixes.DestinationIpv4Builder;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev150305.ipv4.prefixes.destination.ipv4.Ipv4Prefixes;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev150305.ipv4.prefixes.destination.ipv4.Ipv4PrefixesBuilder;
54 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;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.Update;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.UpdateBuilder;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.update.Nlri;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.update.PathAttributes;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.update.WithdrawnRoutes;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.BgpTableType;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.PathAttributes1;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.PathAttributes2;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.MpReachNlri;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.MpReachNlriBuilder;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.MpUnreachNlri;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.MpUnreachNlriBuilder;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.mp.reach.nlri.AdvertizedRoutesBuilder;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.mp.unreach.nlri.WithdrawnRoutesBuilder;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.BgpRib;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.RibId;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.Route;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.Rib;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.RibBuilder;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.RibKey;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.LocRib;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.LocRibBuilder;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.Tables;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.TablesKey;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.Ipv4AddressFamily;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.UnicastSubsequentAddressFamily;
81 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
82 import org.slf4j.Logger;
83 import org.slf4j.LoggerFactory;
86 public final class RIBImpl extends DefaultRibReference implements AutoCloseable, RIB, TransactionChainListener {
87 private static final Logger LOG = LoggerFactory.getLogger(RIBImpl.class);
88 private static final Update EOR = new UpdateBuilder().build();
89 private static final TablesKey IPV4_UNICAST_TABLE = new TablesKey(Ipv4AddressFamily.class, UnicastSubsequentAddressFamily.class);
92 * FIXME: performance: this needs to be turned into a Peer->offset map.
93 * The offset is used to locate a the per-peer state entry in the
96 * For the first release, that map is updated whenever configuration
97 * changes and remains constant on peer flaps. On re-configuration
98 * a resize task is scheduled, so large tables may take some time
99 * before they continue reacting to updates.
101 * For subsequent releases, if we make the reformat process concurrent,
102 * we can trigger reformats when Graceful Restart Time expires for a
105 private final ConcurrentMap<Peer, AdjRIBsOut> ribOuts = new ConcurrentHashMap<>();
106 private final ReconnectStrategyFactory tcpStrategyFactory;
107 private final ReconnectStrategyFactory sessionStrategyFactory;
110 * BGP Best Path selection comparator for ingress best path selection.
112 private final BGPObjectComparator comparator;
113 private final BGPDispatcher dispatcher;
114 private final BindingTransactionChain chain;
115 private final AsNumber localAs;
116 private final Ipv4Address bgpIdentifier;
117 private final Set<BgpTableType> localTables;
118 private final RIBTables tables;
119 private final BlockingQueue<Peer> peers;
120 private final DataBroker dataBroker;
122 private final Runnable scheduler = new Runnable() {
126 final Peer peer = RIBImpl.this.peers.take();
127 LOG.debug("Advertizing loc-rib to new peer {}.", peer);
128 for (final BgpTableType key : RIBImpl.this.localTables) {
130 synchronized (RIBImpl.this) {
131 final AdjRIBsTransactionImpl trans = new AdjRIBsTransactionImpl(RIBImpl.this.ribOuts, RIBImpl.this.comparator, RIBImpl.this.chain.newWriteOnlyTransaction());
132 final AbstractAdjRIBs<?, ?, ?> adj = (AbstractAdjRIBs<?, ?, ?>) RIBImpl.this.tables.get(new TablesKey(key.getAfi(), key.getSafi()));
133 adj.addAllEntries(trans);
134 Futures.addCallback(trans.commit(), new FutureCallback<Void>() {
136 public void onSuccess(final Void result) {
137 LOG.trace("Advertizing {} to peer {} committed successfully", key.getAfi(), peer);
140 public void onFailure(final Throwable t) {
141 LOG.error("Failed to update peer {} with RIB {}", peer, t);
146 } catch (final InterruptedException e) {
147 LOG.info("Scheduler thread was interrupted.", e);
152 public RIBImpl(final RibId ribId, final AsNumber localAs, final Ipv4Address localBgpId, final RIBExtensionConsumerContext extensions,
153 final BGPDispatcher dispatcher, final ReconnectStrategyFactory tcpStrategyFactory,
154 final ReconnectStrategyFactory sessionStrategyFactory, final DataBroker dps, final List<BgpTableType> localTables) {
155 super(InstanceIdentifier.builder(BgpRib.class).child(Rib.class, new RibKey(Preconditions.checkNotNull(ribId))).build());
156 this.chain = dps.createTransactionChain(this);
157 this.localAs = Preconditions.checkNotNull(localAs);
158 this.comparator = new BGPObjectComparator(localAs);
159 this.bgpIdentifier = Preconditions.checkNotNull(localBgpId);
160 this.dispatcher = Preconditions.checkNotNull(dispatcher);
161 this.sessionStrategyFactory = Preconditions.checkNotNull(sessionStrategyFactory);
162 this.tcpStrategyFactory = Preconditions.checkNotNull(tcpStrategyFactory);
163 this.localTables = ImmutableSet.copyOf(localTables);
164 this.tables = new RIBTables(extensions);
165 this.peers = new LinkedBlockingQueue<>();
166 this.dataBroker = dps;
168 LOG.debug("Instantiating RIB table {} at {}", ribId, getInstanceIdentifier());
170 final WriteTransaction trans = this.chain.newWriteOnlyTransaction();
172 // put empty BgpRib if not exists
173 trans.put(LogicalDatastoreType.OPERATIONAL, getInstanceIdentifier(), new RibBuilder().setKey(new RibKey(ribId)).setId(ribId).setLocRib(
174 new LocRibBuilder().setTables(Collections.<Tables> emptyList()).build()).build(), true);
176 for (final BgpTableType t : localTables) {
177 final TablesKey key = new TablesKey(t.getAfi(), t.getSafi());
178 if (this.tables.create(trans, this, key) == null) {
179 LOG.debug("Did not create local table for unhandled table type {}", t);
183 Futures.addCallback(trans.submit(), new FutureCallback<Void>() {
185 public void onSuccess(final Void result) {
186 LOG.trace("Change committed successfully");
190 public void onFailure(final Throwable t) {
191 LOG.error("Failed to initiate RIB {}", getInstanceIdentifier(), t);
196 synchronized void initTables(final byte[] remoteBgpId) {
200 public synchronized void updateTables(final Peer peer, final Update message) {
201 final AdjRIBsTransactionImpl trans = new AdjRIBsTransactionImpl(this.ribOuts, this.comparator, this.chain.newWriteOnlyTransaction());
203 if (!EOR.equals(message)) {
204 final WithdrawnRoutes wr = message.getWithdrawnRoutes();
206 final AdjRIBsIn<?, ?> ari = this.tables.get(IPV4_UNICAST_TABLE);
209 * create MPUnreach for the routes to be handled in the same way as linkstate routes
211 final List<Ipv4Prefixes> prefixes = new ArrayList<>();
212 for (final Ipv4Prefix p : wr.getWithdrawnRoutes()) {
213 prefixes.add(new Ipv4PrefixesBuilder().setPrefix(p).build());
218 new MpUnreachNlriBuilder().setAfi(Ipv4AddressFamily.class).setSafi(UnicastSubsequentAddressFamily.class).setWithdrawnRoutes(
219 new WithdrawnRoutesBuilder().setDestinationType(
220 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(
221 new DestinationIpv4Builder().setIpv4Prefixes(prefixes).build()).build()).build()).build());
223 LOG.debug("Not removing objects from unhandled IPv4 Unicast");
227 final PathAttributes attrs = message.getPathAttributes();
229 final PathAttributes2 mpu = attrs.getAugmentation(PathAttributes2.class);
231 final MpUnreachNlri nlri = mpu.getMpUnreachNlri();
232 final AdjRIBsIn<?, ?> ari = this.tables.get(new TablesKey(nlri.getAfi(), nlri.getSafi()));
233 // EOR messages do not contain withdrawn routes
234 if (nlri.getWithdrawnRoutes() != null) {
236 ari.removeRoutes(trans, peer, nlri);
238 LOG.debug("Not removing objects from unhandled NLRI {}", nlri);
241 ari.markUptodate(trans, peer);
246 final Nlri ar = message.getNlri();
248 final AdjRIBsIn<?, ?> ari = this.tables.get(IPV4_UNICAST_TABLE);
251 * create MPReach for the routes to be handled in the same way as linkstate routes
253 final List<Ipv4Prefixes> prefixes = new ArrayList<>();
254 for (final Ipv4Prefix p : ar.getNlri()) {
255 prefixes.add(new Ipv4PrefixesBuilder().setPrefix(p).build());
257 final MpReachNlriBuilder b = new MpReachNlriBuilder().setAfi(Ipv4AddressFamily.class).setSafi(
258 UnicastSubsequentAddressFamily.class).setAdvertizedRoutes(
259 new AdvertizedRoutesBuilder().setDestinationType(
260 new DestinationIpv4CaseBuilder().setDestinationIpv4(
261 new DestinationIpv4Builder().setIpv4Prefixes(prefixes).build()).build()).build());
263 b.setCNextHop(attrs.getCNextHop());
266 ari.addRoutes(trans, peer, b.build(), attrs);
268 LOG.debug("Not adding objects from unhandled IPv4 Unicast");
273 final PathAttributes1 mpr = attrs.getAugmentation(PathAttributes1.class);
275 final MpReachNlri nlri = mpr.getMpReachNlri();
277 final AdjRIBsIn<?, ?> ari = this.tables.get(new TablesKey(nlri.getAfi(), nlri.getSafi()));
279 if (message.equals(ari.endOfRib())) {
280 ari.markUptodate(trans, peer);
282 ari.addRoutes(trans, peer, nlri, attrs);
285 LOG.debug("Not adding objects from unhandled NLRI {}", nlri);
290 final AdjRIBsIn<?, ?> ari = this.tables.get(IPV4_UNICAST_TABLE);
292 ari.markUptodate(trans, peer);
294 LOG.debug("End-of-RIB for IPv4 Unicast ignored");
298 Futures.addCallback(trans.commit(), new FutureCallback<Void>() {
300 public void onSuccess(final Void result) {
301 LOG.debug("RIB modification successfully committed.");
305 public void onFailure(final Throwable t) {
306 LOG.error("Failed to commit RIB modification", t);
312 public synchronized void clearTable(final Peer peer, final TablesKey key) {
313 final AdjRIBsIn<?, ?> ari = this.tables.get(key);
315 final AdjRIBsTransactionImpl trans = new AdjRIBsTransactionImpl(this.ribOuts, this.comparator, this.chain.newWriteOnlyTransaction());
316 ari.clear(trans, peer);
318 Futures.addCallback(trans.commit(), new FutureCallback<Void>() {
320 public void onSuccess(final Void result) {
321 LOG.trace("Table {} cleared successfully", key);
325 public void onFailure(final Throwable t) {
326 LOG.error("Failed to clear table {}", key, t);
333 public String toString() {
334 return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
337 protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
338 return toStringHelper;
341 @SuppressWarnings("unchecked")
342 protected <K, V extends Route> AdjRIBsIn<K, V> getTable(final TablesKey key) {
343 return (AdjRIBsIn<K, V>) this.tables.get(key);
347 public synchronized void close() throws InterruptedException, ExecutionException {
348 final WriteTransaction t = this.chain.newWriteOnlyTransaction();
349 t.delete(LogicalDatastoreType.OPERATIONAL, getInstanceIdentifier());
355 public AsNumber getLocalAs() {
360 public Ipv4Address getBgpIdentifier() {
361 return this.bgpIdentifier;
365 public Set<? extends BgpTableType> getLocalTables() {
366 return this.localTables;
370 public ReconnectStrategyFactory getTcpStrategyFactory() {
371 return this.tcpStrategyFactory;
375 public ReconnectStrategyFactory getSessionStrategyFactory() {
376 return this.sessionStrategyFactory;
380 public BGPDispatcher getDispatcher() {
381 return this.dispatcher;
385 public void initTable(final Peer bgpPeer, final TablesKey key) {
386 // FIXME: BUG-196: support graceful restart
390 public AdjRIBsOutRegistration registerRIBsOut(final Peer peer, final AdjRIBsOut aro) {
391 final AdjRIBsOutRegistration reg = new AdjRIBsOutRegistration(aro) {
393 protected void removeRegistration() {
394 RIBImpl.this.ribOuts.remove(peer, aro);
398 this.ribOuts.put(peer, aro);
399 LOG.debug("Registering this peer {} to RIB-Out {}", peer, this.ribOuts);
401 this.peers.put(peer);
402 new Thread(this.scheduler).start();
403 } catch (final InterruptedException e) {
410 public void onTransactionChainFailed(final TransactionChain<?, ?> chain, final AsyncTransaction<?, ?> transaction, final Throwable cause) {
411 LOG.error("Broken chain in RIB {} transaction {}", getInstanceIdentifier(), transaction.getIdentifier(), cause);
415 public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
416 LOG.info("RIB {} closed successfully", getInstanceIdentifier());
420 public long getRoutesCount(final TablesKey key) {
422 final Optional<Tables> tableMaybe = this.dataBroker.newReadOnlyTransaction().read(LogicalDatastoreType.OPERATIONAL,
423 getInstanceIdentifier().child(LocRib.class).child(Tables.class, key)).checkedGet();
424 if (tableMaybe.isPresent()) {
425 final Tables table = tableMaybe.get();
426 if (table.getRoutes() instanceof Ipv4RoutesCase) {
427 final Ipv4RoutesCase routesCase = (Ipv4RoutesCase) table.getRoutes();
428 if (routesCase.getIpv4Routes() != null && routesCase.getIpv4Routes().getIpv4Route() != null) {
429 return routesCase.getIpv4Routes().getIpv4Route().size();
431 } else if (table.getRoutes() instanceof Ipv6RoutesCase) {
432 final Ipv6RoutesCase routesCase = (Ipv6RoutesCase) table.getRoutes();
433 if (routesCase.getIpv6Routes() != null && routesCase.getIpv6Routes().getIpv6Route() != null) {
434 return routesCase.getIpv6Routes().getIpv6Route().size();
438 } catch (final ReadFailedException e) {