Merge "BUG-2571 : added parser/serializer for flowspec NLRI"
[bgpcep.git] / bgp / rib-impl / src / main / java / org / opendaylight / protocol / bgp / rib / impl / RIBImpl.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.protocol.bgp.rib.impl;
9
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.Collections;
18 import java.util.List;
19 import java.util.Set;
20 import java.util.concurrent.BlockingQueue;
21 import java.util.concurrent.ConcurrentHashMap;
22 import java.util.concurrent.ConcurrentMap;
23 import java.util.concurrent.ExecutionException;
24 import java.util.concurrent.LinkedBlockingQueue;
25 import javax.annotation.concurrent.ThreadSafe;
26 import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;
27 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
28 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
29 import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
30 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
31 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
32 import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
33 import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
34 import org.opendaylight.protocol.bgp.rib.DefaultRibReference;
35 import org.opendaylight.protocol.bgp.rib.impl.spi.AdjRIBsOut;
36 import org.opendaylight.protocol.bgp.rib.impl.spi.AdjRIBsOutRegistration;
37 import org.opendaylight.protocol.bgp.rib.impl.spi.BGPDispatcher;
38 import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
39 import org.opendaylight.protocol.bgp.rib.spi.AbstractAdjRIBs;
40 import org.opendaylight.protocol.bgp.rib.spi.AdjRIBsIn;
41 import org.opendaylight.protocol.bgp.rib.spi.BGPObjectComparator;
42 import org.opendaylight.protocol.bgp.rib.spi.Peer;
43 import org.opendaylight.protocol.bgp.rib.spi.RIBExtensionConsumerContext;
44 import org.opendaylight.protocol.framework.ReconnectStrategyFactory;
45 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.AsNumber;
46 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.Update;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.UpdateBuilder;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.update.Nlri;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.update.PathAttributes;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.update.WithdrawnRoutes;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.BgpTableType;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.PathAttributes1;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.PathAttributes2;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.destination.destination.type.DestinationIpv4CaseBuilder;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.destination.destination.type.destination.ipv4._case.DestinationIpv4Builder;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.MpReachNlri;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.MpReachNlriBuilder;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.MpUnreachNlri;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.MpUnreachNlriBuilder;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.mp.reach.nlri.AdvertizedRoutesBuilder;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.path.attributes.mp.unreach.nlri.WithdrawnRoutesBuilder;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.BgpRib;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.RibId;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.Route;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.Rib;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.RibBuilder;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.RibKey;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.LocRib;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.LocRibBuilder;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.Tables;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.TablesKey;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.tables.routes.Ipv4RoutesCase;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.tables.routes.Ipv6RoutesCase;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.Ipv4AddressFamily;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.UnicastSubsequentAddressFamily;
77 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
78 import org.slf4j.Logger;
79 import org.slf4j.LoggerFactory;
80
81 @ThreadSafe
82 public final class RIBImpl extends DefaultRibReference implements AutoCloseable, RIB, TransactionChainListener {
83     private static final Logger LOG = LoggerFactory.getLogger(RIBImpl.class);
84     private static final Update EOR = new UpdateBuilder().build();
85     private static final TablesKey IPV4_UNICAST_TABLE = new TablesKey(Ipv4AddressFamily.class, UnicastSubsequentAddressFamily.class);
86
87     /*
88      * FIXME: performance: this needs to be turned into a Peer->offset map.
89      *        The offset is used to locate a the per-peer state entry in the
90      *        RIB tables.
91      *
92      *        For the first release, that map is updated whenever configuration
93      *        changes and remains constant on peer flaps. On re-configuration
94      *        a resize task is scheduled, so large tables may take some time
95      *        before they continue reacting to updates.
96      *
97      *        For subsequent releases, if we make the reformat process concurrent,
98      *        we can trigger reformats when Graceful Restart Time expires for a
99      *        particular peer.
100      */
101     private final ConcurrentMap<Peer, AdjRIBsOut> ribOuts = new ConcurrentHashMap<>();
102     private final ReconnectStrategyFactory tcpStrategyFactory;
103     private final ReconnectStrategyFactory sessionStrategyFactory;
104
105     /**
106      * BGP Best Path selection comparator for ingress best path selection.
107      */
108     private final BGPObjectComparator comparator;
109     private final BGPDispatcher dispatcher;
110     private final BindingTransactionChain chain;
111     private final AsNumber localAs;
112     private final Ipv4Address bgpIdentifier;
113     private final Set<BgpTableType> localTables;
114     private final RIBTables tables;
115     private final BlockingQueue<Peer> peers;
116     private final DataBroker dataBroker;
117
118     private final Runnable scheduler = new Runnable() {
119         @Override
120         public void run() {
121             try {
122                 final Peer peer = RIBImpl.this.peers.take();
123                 LOG.debug("Advertizing loc-rib to new peer {}.", peer);
124                 for (final BgpTableType key : RIBImpl.this.localTables) {
125
126                     synchronized (RIBImpl.this) {
127                         final AdjRIBsTransactionImpl trans = new AdjRIBsTransactionImpl(RIBImpl.this.ribOuts, RIBImpl.this.comparator, RIBImpl.this.chain.newWriteOnlyTransaction());
128                         final AbstractAdjRIBs<?, ?, ?> adj = (AbstractAdjRIBs<?, ?, ?>) RIBImpl.this.tables.get(new TablesKey(key.getAfi(), key.getSafi()));
129                         adj.addAllEntries(trans);
130                         Futures.addCallback(trans.commit(), new FutureCallback<Void>() {
131                             @Override
132                             public void onSuccess(final Void result) {
133                                 LOG.trace("Advertizing {} to peer {} committed successfully", key.getAfi(), peer);
134                             }
135                             @Override
136                             public void onFailure(final Throwable t) {
137                                 LOG.error("Failed to update peer {} with RIB {}", peer, t);
138                             }
139                         });
140                     }
141                 }
142             } catch (final InterruptedException e) {
143                 LOG.info("Scheduler thread was interrupted.", e);
144             }
145         }
146     };
147
148     public RIBImpl(final RibId ribId, final AsNumber localAs, final Ipv4Address localBgpId, final RIBExtensionConsumerContext extensions,
149         final BGPDispatcher dispatcher, final ReconnectStrategyFactory tcpStrategyFactory,
150         final ReconnectStrategyFactory sessionStrategyFactory, final DataBroker dps, final List<BgpTableType> localTables) {
151         super(InstanceIdentifier.builder(BgpRib.class).child(Rib.class, new RibKey(Preconditions.checkNotNull(ribId))).build());
152         this.chain = dps.createTransactionChain(this);
153         this.localAs = Preconditions.checkNotNull(localAs);
154         this.comparator = new BGPObjectComparator(localAs);
155         this.bgpIdentifier = Preconditions.checkNotNull(localBgpId);
156         this.dispatcher = Preconditions.checkNotNull(dispatcher);
157         this.sessionStrategyFactory = Preconditions.checkNotNull(sessionStrategyFactory);
158         this.tcpStrategyFactory = Preconditions.checkNotNull(tcpStrategyFactory);
159         this.localTables = ImmutableSet.copyOf(localTables);
160         this.tables = new RIBTables(extensions);
161         this.peers = new LinkedBlockingQueue<>();
162         this.dataBroker = dps;
163
164         LOG.debug("Instantiating RIB table {} at {}", ribId, getInstanceIdentifier());
165
166         final WriteTransaction trans = this.chain.newWriteOnlyTransaction();
167
168         // put empty BgpRib if not exists
169         trans.put(LogicalDatastoreType.OPERATIONAL, getInstanceIdentifier(), new RibBuilder().setKey(new RibKey(ribId)).setId(ribId).setLocRib(
170             new LocRibBuilder().setTables(Collections.<Tables> emptyList()).build()).build(), true);
171
172         for (final BgpTableType t : localTables) {
173             final TablesKey key = new TablesKey(t.getAfi(), t.getSafi());
174             if (this.tables.create(trans, this, key) == null) {
175                 LOG.debug("Did not create local table for unhandled table type {}", t);
176             }
177         }
178
179         Futures.addCallback(trans.submit(), new FutureCallback<Void>() {
180             @Override
181             public void onSuccess(final Void result) {
182                 LOG.trace("Change committed successfully");
183             }
184
185             @Override
186             public void onFailure(final Throwable t) {
187                 LOG.error("Failed to initiate RIB {}", getInstanceIdentifier(), t);
188             }
189         });
190     }
191
192     synchronized void initTables(final byte[] remoteBgpId) {
193     }
194
195     @Override
196     public synchronized void updateTables(final Peer peer, final Update message) {
197         final AdjRIBsTransactionImpl trans = new AdjRIBsTransactionImpl(this.ribOuts, this.comparator, this.chain.newWriteOnlyTransaction());
198
199         if (!EOR.equals(message)) {
200             final WithdrawnRoutes wr = message.getWithdrawnRoutes();
201             if (wr != null) {
202                 final AdjRIBsIn<?, ?> ari = this.tables.get(IPV4_UNICAST_TABLE);
203                 if (ari != null) {
204                     /*
205                      * create MPUnreach for the routes to be handled in the same way as linkstate routes
206                      */
207                     ari.removeRoutes(
208                         trans,
209                         peer,
210                         new MpUnreachNlriBuilder().setAfi(Ipv4AddressFamily.class).setSafi(UnicastSubsequentAddressFamily.class).setWithdrawnRoutes(
211                             new WithdrawnRoutesBuilder().setDestinationType(
212                                 new DestinationIpv4CaseBuilder().setDestinationIpv4(
213                                     new DestinationIpv4Builder().setIpv4Prefixes(wr.getWithdrawnRoutes()).build()).build()).build()).build());
214                 } else {
215                     LOG.debug("Not removing objects from unhandled IPv4 Unicast");
216                 }
217             }
218
219             final PathAttributes attrs = message.getPathAttributes();
220             if (attrs != null) {
221                 final PathAttributes2 mpu = attrs.getAugmentation(PathAttributes2.class);
222                 if (mpu != null) {
223                     final MpUnreachNlri nlri = mpu.getMpUnreachNlri();
224                     final AdjRIBsIn<?, ?> ari = this.tables.get(new TablesKey(nlri.getAfi(), nlri.getSafi()));
225                     // EOR messages do not contain withdrawn routes
226                     if (nlri.getWithdrawnRoutes() != null) {
227                         if (ari != null) {
228                             ari.removeRoutes(trans, peer, nlri);
229                         } else {
230                             LOG.debug("Not removing objects from unhandled NLRI {}", nlri);
231                         }
232                     } else {
233                         ari.markUptodate(trans, peer);
234                     }
235                 }
236             }
237
238             final Nlri ar = message.getNlri();
239             if (ar != null) {
240                 final AdjRIBsIn<?, ?> ari = this.tables.get(IPV4_UNICAST_TABLE);
241                 if (ari != null) {
242                     /*
243                      * create MPReach for the routes to be handled in the same way as linkstate routes
244                      */
245                     final MpReachNlriBuilder b = new MpReachNlriBuilder().setAfi(Ipv4AddressFamily.class).setSafi(
246                         UnicastSubsequentAddressFamily.class).setAdvertizedRoutes(
247                             new AdvertizedRoutesBuilder().setDestinationType(
248                                 new DestinationIpv4CaseBuilder().setDestinationIpv4(
249                                     new DestinationIpv4Builder().setIpv4Prefixes(ar.getNlri()).build()).build()).build());
250                     if (attrs != null) {
251                         b.setCNextHop(attrs.getCNextHop());
252                     }
253
254                     ari.addRoutes(trans, peer, b.build(), attrs);
255                 } else {
256                     LOG.debug("Not adding objects from unhandled IPv4 Unicast");
257                 }
258             }
259
260             if (attrs != null) {
261                 final PathAttributes1 mpr = attrs.getAugmentation(PathAttributes1.class);
262                 if (mpr != null) {
263                     final MpReachNlri nlri = mpr.getMpReachNlri();
264
265                     final AdjRIBsIn<?, ?> ari = this.tables.get(new TablesKey(nlri.getAfi(), nlri.getSafi()));
266                     if (ari != null) {
267                         if (message.equals(ari.endOfRib())) {
268                             ari.markUptodate(trans, peer);
269                         } else {
270                             ari.addRoutes(trans, peer, nlri, attrs);
271                         }
272                     } else {
273                         LOG.debug("Not adding objects from unhandled NLRI {}", nlri);
274                     }
275                 }
276             }
277         } else {
278             final AdjRIBsIn<?, ?> ari = this.tables.get(IPV4_UNICAST_TABLE);
279             if (ari != null) {
280                 ari.markUptodate(trans, peer);
281             } else {
282                 LOG.debug("End-of-RIB for IPv4 Unicast ignored");
283             }
284         }
285
286         Futures.addCallback(trans.commit(), new FutureCallback<Void>() {
287             @Override
288             public void onSuccess(final Void result) {
289                 LOG.debug("RIB modification successfully committed.");
290             }
291
292             @Override
293             public void onFailure(final Throwable t) {
294                 LOG.error("Failed to commit RIB modification", t);
295             }
296         });
297     }
298
299     @Override
300     public synchronized void clearTable(final Peer peer, final TablesKey key) {
301         final AdjRIBsIn<?, ?> ari = this.tables.get(key);
302         if (ari != null) {
303             final AdjRIBsTransactionImpl trans = new AdjRIBsTransactionImpl(this.ribOuts, this.comparator, this.chain.newWriteOnlyTransaction());
304             ari.clear(trans, peer);
305
306             Futures.addCallback(trans.commit(), new FutureCallback<Void>() {
307                 @Override
308                 public void onSuccess(final Void result) {
309                     LOG.trace("Table {} cleared successfully", key);
310                 }
311
312                 @Override
313                 public void onFailure(final Throwable t) {
314                     LOG.error("Failed to clear table {}", key, t);
315                 }
316             });
317         }
318     }
319
320     @Override
321     public String toString() {
322         return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
323     }
324
325     protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
326         return toStringHelper;
327     }
328
329     @SuppressWarnings("unchecked")
330     protected <K, V extends Route> AdjRIBsIn<K, V> getTable(final TablesKey key) {
331         return (AdjRIBsIn<K, V>) this.tables.get(key);
332     }
333
334     @Override
335     public synchronized void close() throws InterruptedException, ExecutionException {
336         final WriteTransaction t = this.chain.newWriteOnlyTransaction();
337         t.delete(LogicalDatastoreType.OPERATIONAL, getInstanceIdentifier());
338         t.submit().get();
339         this.chain.close();
340     }
341
342     @Override
343     public AsNumber getLocalAs() {
344         return this.localAs;
345     }
346
347     @Override
348     public Ipv4Address getBgpIdentifier() {
349         return this.bgpIdentifier;
350     }
351
352     @Override
353     public Set<? extends BgpTableType> getLocalTables() {
354         return this.localTables;
355     }
356
357     @Override
358     public ReconnectStrategyFactory getTcpStrategyFactory() {
359         return this.tcpStrategyFactory;
360     }
361
362     @Override
363     public ReconnectStrategyFactory getSessionStrategyFactory() {
364         return this.sessionStrategyFactory;
365     }
366
367     @Override
368     public BGPDispatcher getDispatcher() {
369         return this.dispatcher;
370     }
371
372     @Override
373     public void initTable(final Peer bgpPeer, final TablesKey key) {
374         // FIXME: BUG-196: support graceful restart
375     }
376
377     @Override
378     public AdjRIBsOutRegistration registerRIBsOut(final Peer peer, final AdjRIBsOut aro) {
379         final AdjRIBsOutRegistration reg = new AdjRIBsOutRegistration(aro) {
380             @Override
381             protected void removeRegistration() {
382                 RIBImpl.this.ribOuts.remove(peer, aro);
383             }
384         };
385
386         this.ribOuts.put(peer, aro);
387         LOG.debug("Registering this peer {} to RIB-Out {}", peer, this.ribOuts);
388         try {
389             this.peers.put(peer);
390             new Thread(this.scheduler).start();
391         } catch (final InterruptedException e) {
392             //
393         }
394         return reg;
395     }
396
397     @Override
398     public void onTransactionChainFailed(final TransactionChain<?, ?> chain, final AsyncTransaction<?, ?> transaction, final Throwable cause) {
399         LOG.error("Broken chain in RIB {} transaction {}", getInstanceIdentifier(), transaction.getIdentifier(), cause);
400     }
401
402     @Override
403     public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
404         LOG.info("RIB {} closed successfully", getInstanceIdentifier());
405     }
406
407     @Override
408     public long getRoutesCount(final TablesKey key) {
409         try {
410             final Optional<Tables> tableMaybe = this.dataBroker.newReadOnlyTransaction().read(LogicalDatastoreType.OPERATIONAL,
411                     getInstanceIdentifier().child(LocRib.class).child(Tables.class, key)).checkedGet();
412             if (tableMaybe.isPresent()) {
413                 final Tables table = tableMaybe.get();
414                 if (table.getRoutes() instanceof Ipv4RoutesCase) {
415                     final Ipv4RoutesCase routesCase = (Ipv4RoutesCase) table.getRoutes();
416                     if (routesCase.getIpv4Routes() != null && routesCase.getIpv4Routes().getIpv4Route() != null) {
417                         return routesCase.getIpv4Routes().getIpv4Route().size();
418                     }
419                 } else if (table.getRoutes() instanceof Ipv6RoutesCase) {
420                     final Ipv6RoutesCase routesCase = (Ipv6RoutesCase) table.getRoutes();
421                     if (routesCase.getIpv6Routes() != null && routesCase.getIpv6Routes().getIpv6Route() != null) {
422                         return routesCase.getIpv6Routes().getIpv6Route().size();
423                     }
424                 }
425             }
426         } catch (final ReadFailedException e) {
427             //no-op
428         }
429         return 0;
430     }
431 }