Update MRI projects for Aluminium
[bgpcep.git] / bgp / rib-impl / src / main / java / org / opendaylight / protocol / bgp / rib / impl / BGPPeer.java
1 /*
2  * Copyright (c) 2014 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 static java.util.Objects.requireNonNull;
11
12 import com.google.common.base.MoreObjects;
13 import com.google.common.base.Stopwatch;
14 import com.google.common.cache.CacheBuilder;
15 import com.google.common.cache.CacheLoader;
16 import com.google.common.cache.LoadingCache;
17 import com.google.common.collect.ImmutableMap;
18 import com.google.common.collect.ImmutableSet;
19 import com.google.common.collect.Sets;
20 import com.google.common.net.InetAddresses;
21 import com.google.common.util.concurrent.FluentFuture;
22 import com.google.common.util.concurrent.Futures;
23 import com.google.common.util.concurrent.ListenableFuture;
24 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
25 import java.util.ArrayList;
26 import java.util.Collections;
27 import java.util.HashMap;
28 import java.util.HashSet;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Objects;
32 import java.util.Optional;
33 import java.util.Set;
34 import java.util.concurrent.TimeUnit;
35 import java.util.stream.Collectors;
36 import org.checkerframework.checker.lock.qual.GuardedBy;
37 import org.checkerframework.checker.lock.qual.Holding;
38 import org.opendaylight.mdsal.binding.api.RpcProviderService;
39 import org.opendaylight.mdsal.binding.api.Transaction;
40 import org.opendaylight.mdsal.binding.api.TransactionChain;
41 import org.opendaylight.mdsal.common.api.CommitInfo;
42 import org.opendaylight.mdsal.dom.api.DOMDataTreeTransaction;
43 import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
44 import org.opendaylight.protocol.bgp.openconfig.spi.BGPTableTypeRegistryConsumer;
45 import org.opendaylight.protocol.bgp.parser.BGPDocumentedException;
46 import org.opendaylight.protocol.bgp.parser.BGPError;
47 import org.opendaylight.protocol.bgp.parser.impl.message.update.LocalPreferenceAttributeParser;
48 import org.opendaylight.protocol.bgp.parser.spi.MessageUtil;
49 import org.opendaylight.protocol.bgp.parser.spi.RevisedErrorHandlingSupport;
50 import org.opendaylight.protocol.bgp.rib.impl.config.BgpPeer;
51 import org.opendaylight.protocol.bgp.rib.impl.config.GracefulRestartUtil;
52 import org.opendaylight.protocol.bgp.rib.impl.spi.BGPSessionPreferences;
53 import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
54 import org.opendaylight.protocol.bgp.rib.impl.state.BGPSessionStateProvider;
55 import org.opendaylight.protocol.bgp.rib.spi.BGPSession;
56 import org.opendaylight.protocol.bgp.rib.spi.BGPSessionListener;
57 import org.opendaylight.protocol.bgp.rib.spi.BGPTerminationReason;
58 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
59 import org.opendaylight.protocol.bgp.rib.spi.RouterIds;
60 import org.opendaylight.protocol.bgp.rib.spi.state.BGPSessionState;
61 import org.opendaylight.protocol.bgp.rib.spi.state.BGPTimersState;
62 import org.opendaylight.protocol.bgp.rib.spi.state.BGPTransportState;
63 import org.opendaylight.protocol.util.Ipv4Util;
64 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.AsNumber;
65 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddressNoZone;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev180329.ipv4.prefixes.DestinationIpv4Builder;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev180329.ipv4.prefixes.destination.ipv4.Ipv4Prefixes;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev180329.ipv4.prefixes.destination.ipv4.Ipv4PrefixesBuilder;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev180329.update.attributes.mp.reach.nlri.advertized.routes.destination.type.DestinationIpv4CaseBuilder;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev200120.Update;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev200120.open.message.BgpParameters;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev200120.path.attributes.Attributes;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev200120.path.attributes.AttributesBuilder;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev200120.update.message.Nlri;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.BgpAddPathTableType;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.BgpTableType;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.RouteRefresh;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.SendReceive;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.mp.capabilities.GracefulRestartCapability;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.mp.capabilities.add.path.capability.AddressFamilies;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpReachNlri;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpReachNlriBuilder;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpUnreachNlri;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpUnreachNlriBuilder;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.mp.reach.nlri.AdvertizedRoutesBuilder;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.mp.unreach.nlri.WithdrawnRoutesBuilder;
87 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.peer.rpc.rev180329.BgpPeerRpcService;
88 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.PeerRole;
89 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.PeerKey;
90 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.peer.AdjRibOut;
91 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.Tables;
92 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.TablesKey;
93 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.AddressFamily;
94 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.ClusterIdentifier;
95 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.Ipv4AddressFamily;
96 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.RouteTarget;
97 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.SubsequentAddressFamily;
98 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.UnicastSubsequentAddressFamily;
99 import org.opendaylight.yangtools.concepts.ObjectRegistration;
100 import org.opendaylight.yangtools.concepts.Registration;
101 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
102 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
103 import org.opendaylight.yangtools.yang.binding.Notification;
104 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
105 import org.slf4j.Logger;
106 import org.slf4j.LoggerFactory;
107
108 /**
109  * Class representing a peer. We have a single instance for each peer, which provides translation from BGP events into
110  * RIB actions.
111  */
112 public class BGPPeer extends AbstractPeer implements BGPSessionListener {
113     private static final Logger LOG = LoggerFactory.getLogger(BGPPeer.class);
114     private static final TablesKey IPV4_UCAST_TABLE_KEY = new TablesKey(Ipv4AddressFamily.class,
115         UnicastSubsequentAddressFamily.class);
116
117     private ImmutableSet<TablesKey> tables = ImmutableSet.of();
118     private final RIB rib;
119     private final Map<TablesKey, AdjRibOutListener> adjRibOutListenerSet = new HashMap<>();
120     private final List<RouteTarget> rtMemberships = new ArrayList<>();
121     private final RpcProviderService rpcRegistry;
122     private final BGPTableTypeRegistryConsumer tableTypeRegistry;
123     private final BgpPeer bgpPeer;
124     private InstanceIdentifier<AdjRibOut> peerRibOutIId;
125     private KeyedInstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib
126             .rev180329.bgp.rib.rib.Peer, PeerKey> peerIId;
127     @GuardedBy("this")
128     private Registration trackerRegistration;
129     private final LoadingCache<TablesKey, KeyedInstanceIdentifier<Tables, TablesKey>> tablesIId
130             = CacheBuilder.newBuilder()
131             .build(new CacheLoader<TablesKey, KeyedInstanceIdentifier<Tables, TablesKey>>() {
132                 @Override
133                 public KeyedInstanceIdentifier<Tables, TablesKey> load(final TablesKey tablesKey) {
134                     return BGPPeer.this.peerRibOutIId.child(Tables.class, tablesKey);
135                 }
136             });
137
138     @GuardedBy("this")
139     private BGPSession currentSession;
140     @GuardedBy("this")
141     private AdjRibInWriter ribWriter;
142     @GuardedBy("this")
143     private EffectiveRibInWriter effRibInWriter;
144     private ObjectRegistration<BgpPeerRpcService> rpcRegistration;
145     private Map<TablesKey, SendReceive> addPathTableMaps = Collections.emptyMap();
146     private YangInstanceIdentifier peerPath;
147     private boolean sessionUp;
148     private boolean llgrSupport;
149     private Stopwatch peerRestartStopwatch;
150     private long currentSelectionDeferralTimerSeconds;
151     private final List<TablesKey> missingEOT = new ArrayList<>();
152
153     public BGPPeer(
154             final BGPTableTypeRegistryConsumer tableTypeRegistry,
155             final IpAddressNoZone neighborAddress,
156             final String peerGroupName,
157             final RIB rib,
158             final PeerRole role,
159             final ClusterIdentifier clusterId,
160             final AsNumber localAs,
161             final RpcProviderService rpcRegistry,
162             final Set<TablesKey> afiSafisAdvertized,
163             final Set<TablesKey> afiSafisGracefulAdvertized,
164             final Map<TablesKey, Integer> llGracefulTablesAdvertised,
165             final BgpPeer bgpPeer) {
166         super(rib, Ipv4Util.toStringIP(neighborAddress), peerGroupName, role, clusterId,
167                 localAs, neighborAddress, afiSafisAdvertized, afiSafisGracefulAdvertized, llGracefulTablesAdvertised);
168         this.tableTypeRegistry = requireNonNull(tableTypeRegistry);
169         this.rib = requireNonNull(rib);
170         this.rpcRegistry = rpcRegistry;
171         this.bgpPeer = bgpPeer;
172     }
173
174     private static Attributes nextHopToAttribute(final Attributes attrs, final MpReachNlri mpReach) {
175         if (attrs.getCNextHop() == null && mpReach.getCNextHop() != null) {
176             final AttributesBuilder attributesBuilder = new AttributesBuilder(attrs);
177             attributesBuilder.setCNextHop(mpReach.getCNextHop());
178             return attributesBuilder.build();
179         }
180         return attrs;
181     }
182
183     /**
184      * Creates MPReach for the prefixes to be handled in the same way as linkstate routes.
185      *
186      * @param message Update message containing prefixes in NLRI
187      * @return MpReachNlri with prefixes from the nlri field
188      */
189     private static MpReachNlri prefixesToMpReach(final Update message) {
190         final List<Ipv4Prefixes> prefixes = message.getNlri().stream()
191                 .map(n -> new Ipv4PrefixesBuilder().setPrefix(n.getPrefix()).setPathId(n.getPathId()).build())
192                 .collect(Collectors.toList());
193         final MpReachNlriBuilder b = new MpReachNlriBuilder().setAfi(Ipv4AddressFamily.class).setSafi(
194                 UnicastSubsequentAddressFamily.class).setAdvertizedRoutes(
195                 new AdvertizedRoutesBuilder().setDestinationType(
196                         new DestinationIpv4CaseBuilder().setDestinationIpv4(
197                                 new DestinationIpv4Builder().setIpv4Prefixes(prefixes).build()).build()).build());
198         if (message.getAttributes() != null) {
199             b.setCNextHop(message.getAttributes().getCNextHop());
200         }
201         return b.build();
202     }
203
204     /**
205      * Create MPUnreach for the prefixes to be handled in the same way as linkstate routes.
206      *
207      * @param message            Update message containing withdrawn routes
208      * @param isAnyNlriAnnounced isAnyNlriAnnounced
209      * @return MpUnreachNlri with prefixes from the withdrawn routes field
210      */
211     private static MpUnreachNlri prefixesToMpUnreach(final Update message, final boolean isAnyNlriAnnounced) {
212         final List<Ipv4Prefixes> prefixes = new ArrayList<>();
213         message.getWithdrawnRoutes().forEach(w -> {
214
215             Optional<Nlri> nlriAnounced = Optional.empty();
216             if (isAnyNlriAnnounced) {
217                 nlriAnounced = message.getNlri().stream().filter(n -> Objects.equals(n.getPrefix(), w.getPrefix())
218                         && Objects.equals(n.getPathId(), w.getPathId()))
219                         .findAny();
220             }
221             if (!nlriAnounced.isPresent()) {
222                 prefixes.add(new Ipv4PrefixesBuilder().setPrefix(w.getPrefix()).setPathId(w.getPathId()).build());
223             }
224         });
225         return new MpUnreachNlriBuilder().setAfi(Ipv4AddressFamily.class).setSafi(UnicastSubsequentAddressFamily.class)
226                 .setWithdrawnRoutes(new WithdrawnRoutesBuilder().setDestinationType(new org.opendaylight.yang.gen.v1
227                         .urn.opendaylight.params.xml.ns.yang.bgp.inet.rev180329.update.attributes.mp.unreach.nlri
228                         .withdrawn.routes.destination.type.DestinationIpv4CaseBuilder().setDestinationIpv4(
229                         new DestinationIpv4Builder().setIpv4Prefixes(prefixes).build()).build()).build()).build();
230     }
231
232     private static Map<TablesKey, SendReceive> mapTableTypesFamilies(final List<AddressFamilies> addPathTablesType) {
233         return ImmutableMap.copyOf(addPathTablesType.stream().collect(Collectors.toMap(af -> new TablesKey(af.getAfi(),
234                         af.getSafi()), BgpAddPathTableType::getSendReceive)));
235     }
236
237     public synchronized void instantiateServiceInstance() {
238         createDomChain();
239         this.ribWriter = AdjRibInWriter.create(this.rib.getYangRibId(), this.peerRole, this);
240         setActive(true);
241     }
242
243     @Override
244     public synchronized FluentFuture<? extends CommitInfo> close() {
245         final FluentFuture<? extends CommitInfo> future = releaseConnection();
246         closeDomChain();
247         setActive(false);
248         return future;
249     }
250
251     @Override
252     public void onMessage(final BGPSession session, final Notification msg) throws BGPDocumentedException {
253         if (msg instanceof Update) {
254             onUpdateMessage((Update) msg);
255         } else if (msg instanceof RouteRefresh) {
256             onRouteRefreshMessage((RouteRefresh) msg);
257         } else {
258             LOG.info("Ignoring unhandled message class {}", msg.getClass());
259         }
260     }
261
262     private void onRouteRefreshMessage(final RouteRefresh message) {
263         final Class<? extends AddressFamily> rrAfi = message.getAfi();
264         final Class<? extends SubsequentAddressFamily> rrSafi = message.getSafi();
265
266         final TablesKey key = new TablesKey(rrAfi, rrSafi);
267         synchronized (this) {
268             final AdjRibOutListener listener = this.adjRibOutListenerSet.remove(key);
269             if (listener != null) {
270                 listener.close();
271                 createAdjRibOutListener(key, listener.isMpSupported());
272             } else {
273                 LOG.info("Ignoring RouteRefresh message. Afi/Safi is not supported: {}, {}.", rrAfi, rrSafi);
274             }
275         }
276     }
277
278     /**
279      * Check for presence of well known mandatory attribute LOCAL_PREF in Update message.
280      *
281      * @param message Update message
282      */
283     private void checkMandatoryAttributesPresence(final Update message) throws BGPDocumentedException {
284         if (MessageUtil.isAnyNlriPresent(message)) {
285             final Attributes attrs = message.getAttributes();
286             if (this.peerRole == PeerRole.Ibgp && (attrs == null || attrs.getLocalPref() == null)) {
287                 throw new BGPDocumentedException(BGPError.MANDATORY_ATTR_MISSING_MSG + "LOCAL_PREF",
288                         BGPError.WELL_KNOWN_ATTR_MISSING,
289                         new byte[]{LocalPreferenceAttributeParser.TYPE});
290             }
291         }
292     }
293
294     /**
295      * Process Update message received.
296      * Calls {@link #checkMandatoryAttributesPresence(Update)} to check for presence of mandatory attributes.
297      *
298      * @param message Update message
299      */
300     private synchronized void onUpdateMessage(final Update message) throws BGPDocumentedException {
301         checkMandatoryAttributesPresence(message);
302
303         // update AdjRibs
304         final Attributes attrs = message.getAttributes();
305         MpReachNlri mpReach;
306         final boolean isAnyNlriAnnounced = message.getNlri() != null;
307         if (isAnyNlriAnnounced) {
308             mpReach = prefixesToMpReach(message);
309         } else {
310             mpReach = MessageUtil.getMpReachNlri(attrs);
311         }
312         if (mpReach != null) {
313             this.ribWriter.updateRoutes(mpReach, nextHopToAttribute(attrs, mpReach));
314         }
315         final MpUnreachNlri mpUnreach;
316         if (message.getWithdrawnRoutes() != null) {
317             mpUnreach = prefixesToMpUnreach(message, isAnyNlriAnnounced);
318         } else {
319             mpUnreach = MessageUtil.getMpUnreachNlri(attrs);
320         }
321         final boolean endOfRib = BgpPeerUtil.isEndOfRib(message);
322         if (mpUnreach != null) {
323             if (endOfRib) {
324                 final TablesKey tablesKey = new TablesKey(mpUnreach.getAfi(), mpUnreach.getSafi());
325                 this.ribWriter.removeStaleRoutes(tablesKey);
326                 this.missingEOT.remove(tablesKey);
327                 handleGracefulEndOfRib();
328             } else {
329                 this.ribWriter.removeRoutes(mpUnreach);
330             }
331         } else if (endOfRib) {
332             this.ribWriter.removeStaleRoutes(IPV4_UCAST_TABLE_KEY);
333             this.missingEOT.remove(IPV4_UCAST_TABLE_KEY);
334             handleGracefulEndOfRib();
335         }
336     }
337
338     @Holding("this")
339     private void handleGracefulEndOfRib() {
340         if (isLocalRestarting()) {
341             if (this.missingEOT.isEmpty()) {
342                 createEffRibInWriter();
343                 this.effRibInWriter.init();
344                 registerPrefixesCounters(this.effRibInWriter, this.effRibInWriter);
345                 for (final TablesKey key : getAfiSafisAdvertized()) {
346                     createAdjRibOutListener(key, true);
347                 }
348                 setLocalRestartingState(false);
349                 setGracefulPreferences(false, Collections.emptySet());
350             }
351         }
352     }
353
354     @Override
355     public synchronized void onSessionUp(final BGPSession session) {
356         this.currentSession = session;
357         this.sessionUp = true;
358         this.bindingChain = this.rib.createPeerChain(this);
359         if (this.currentSession instanceof BGPSessionStateProvider) {
360             ((BGPSessionStateProvider) this.currentSession).registerMessagesCounter(this);
361         }
362         final GracefulRestartCapability advertisedGracefulRestartCapability =
363                 session.getAdvertisedGracefulRestartCapability();
364         final var advertisedTables = advertisedGracefulRestartCapability.getTables();
365         final var advertisedLLTables = session.getAdvertisedLlGracefulRestartCapability().getTables();
366
367         final List<AddressFamilies> addPathTablesType = session.getAdvertisedAddPathTableTypes();
368         final Set<BgpTableType> advertizedTableTypes = session.getAdvertisedTableTypes();
369         LOG.info("Session with peer {} went up with tables {} and Add Path tables {}", this.name,
370                 advertizedTableTypes, addPathTablesType);
371         final Set<TablesKey> setTables = advertizedTableTypes.stream().map(t -> new TablesKey(t.getAfi(), t.getSafi()))
372                 .collect(Collectors.toSet());
373         this.tables = ImmutableSet.copyOf(setTables);
374
375         this.addPathTableMaps = mapTableTypesFamilies(addPathTablesType);
376         final boolean restartingLocally = isLocalRestarting();
377
378         if (!isRestartingGracefully()) {
379             this.rawIdentifier = InetAddresses.forString(session.getBgpId().getValue()).getAddress();
380             this.peerId = RouterIds.createPeerId(session.getBgpId());
381             this.peerIId = getInstanceIdentifier().child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns
382                             .yang.bgp.rib.rev180329.bgp.rib.rib.Peer.class, new PeerKey(this.peerId));
383             this.peerPath = createPeerPath();
384             this.peerRibOutIId = peerIId.child(AdjRibOut.class);
385             this.trackerRegistration = this.rib.getPeerTracker().registerPeer(this);
386             createEffRibInWriter();
387             registerPrefixesCounters(this.effRibInWriter, this.effRibInWriter);
388
389             this.effRibInWriter.init();
390             this.ribWriter = this.ribWriter.transform(this.peerId, this.peerPath, this.rib.getRibSupportContext(),
391                     this.tables, this.addPathTableMaps);
392
393             if (this.rpcRegistry != null) {
394                 this.rpcRegistration = this.rpcRegistry.registerRpcImplementation(BgpPeerRpcService.class,
395                     new BgpPeerRpc(this, session, this.tables), ImmutableSet.of(this.rib.getInstanceIdentifier().child(
396                         org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib
397                         .Peer.class, new PeerKey(this.peerId))));
398             }
399         } else {
400             final Set<TablesKey> forwardingTables;
401             if (advertisedTables == null) {
402                 forwardingTables = Collections.emptySet();
403             } else {
404                 forwardingTables = advertisedTables.values().stream()
405                         .filter(table -> table.getAfiFlags() != null)
406                         .filter(table -> table.getAfiFlags().isForwardingState())
407                         .map(table -> new TablesKey(table.getAfi(), table.getSafi()))
408                         .collect(Collectors.toSet());
409             }
410             this.ribWriter.clearTables(Sets.difference(this.tables, forwardingTables));
411             if (restartingLocally) {
412                 this.effRibInWriter.close();
413                 this.peerRestartStopwatch = Stopwatch.createStarted();
414                 handleSelectionReferralTimer();
415                 this.missingEOT.addAll(this.tables);
416             }
417         }
418         if (advertisedTables == null || advertisedTables.isEmpty()) {
419             setAdvertizedGracefulRestartTableTypes(Collections.emptyList());
420         } else {
421             setAdvertizedGracefulRestartTableTypes(advertisedTables.values().stream()
422                     .map(t -> new TablesKey(t.getAfi(), t.getSafi())).collect(Collectors.toList()));
423         }
424         setAfiSafiGracefulRestartState(advertisedGracefulRestartCapability.getRestartTime().toJava(), false,
425             restartingLocally);
426
427         final Map<TablesKey, Integer> llTablesReceived;
428         if (advertisedLLTables != null) {
429             llTablesReceived = new HashMap<>();
430             for (var table : advertisedLLTables.values()) {
431                 llTablesReceived.put(new TablesKey(table.getAfi(), table.getSafi()),
432                     table.getLongLivedStaleTime().getValue().intValue());
433             }
434         } else {
435             llTablesReceived = Collections.emptyMap();
436         }
437         setAdvertizedLlGracefulRestartTableTypes(llTablesReceived);
438
439         if (!llTablesReceived.isEmpty()) {
440             llgrSupport = true;
441             // FIXME: propagate preserved tables
442         } else {
443             // FIXME: clear preserved tables
444             llgrSupport = false;
445         }
446
447         if (!restartingLocally) {
448             addBgp4Support();
449             for (final TablesKey key : getAfiSafisAdvertized()) {
450                 createAdjRibOutListener(key, true);
451             }
452         }
453
454         // SpotBugs does not grok Optional.ifPresent() and thinks we are using unsynchronized access
455         final Optional<RevisedErrorHandlingSupport> errorHandling = this.bgpPeer.getErrorHandling();
456         if (errorHandling.isPresent()) {
457             this.currentSession.addDecoderConstraint(RevisedErrorHandlingSupport.class, errorHandling.get());
458         }
459     }
460
461     private boolean isRestartingGracefully() {
462         return isLocalRestarting() || isPeerRestarting();
463     }
464
465     private synchronized void createEffRibInWriter() {
466         this.effRibInWriter = new EffectiveRibInWriter(this, this.rib,
467             this.rib.createPeerDOMChain(this),
468             this.peerPath, this.tables, this.tableTypeRegistry,
469             this.rtMemberships,
470             this.rtCache);
471     }
472
473     //try to add a support for old-school BGP-4, if peer did not advertise IPv4-Unicast MP capability
474     @Holding("this")
475     private void addBgp4Support() {
476         if (!this.tables.contains(IPV4_UCAST_TABLE_KEY)) {
477             final HashSet<TablesKey> newSet = new HashSet<>(this.tables);
478             newSet.add(IPV4_UCAST_TABLE_KEY);
479             this.tables = ImmutableSet.copyOf(newSet);
480             createAdjRibOutListener(IPV4_UCAST_TABLE_KEY, false);
481         }
482     }
483
484     @Holding("this")
485     private void createAdjRibOutListener(final TablesKey key, final boolean mpSupport) {
486         final RIBSupport<?, ?, ?, ?> ribSupport = this.rib.getRibSupportContext().getRIBSupport(key);
487
488         // not particularly nice
489         if (ribSupport != null && this.currentSession instanceof BGPSessionImpl) {
490             final ChannelOutputLimiter limiter = ((BGPSessionImpl) this.currentSession).getLimiter();
491             final AdjRibOutListener adjRibOut = AdjRibOutListener.create(this.peerId, key,
492                     this.rib.getYangRibId(), this.rib.getCodecsRegistry(), ribSupport,
493                     this.rib.getService(), limiter, mpSupport);
494             this.adjRibOutListenerSet.put(key, adjRibOut);
495             registerPrefixesSentCounter(key, adjRibOut);
496         }
497     }
498
499     @Override
500     public synchronized void onSessionDown(final BGPSession session, final Exception exc) {
501         if (exc.getMessage().equals(BGPSessionImpl.END_OF_INPUT)) {
502             LOG.info("Session with peer {} went down", this.name);
503         } else {
504             LOG.info("Session with peer {} went down", this.name, exc);
505         }
506         releaseConnectionGracefully();
507     }
508
509     @Override
510     public synchronized void onSessionTerminated(final BGPSession session, final BGPTerminationReason cause) {
511         LOG.info("Session with peer {} terminated: {}", this.name, cause);
512         releaseConnectionGracefully();
513     }
514
515     @Override
516     public String toString() {
517         return MoreObjects.toStringHelper(this).add("name", this.name).add("tables", this.tables).toString();
518     }
519
520     @Override
521     public synchronized FluentFuture<? extends CommitInfo> releaseConnection() {
522         LOG.info("Closing session with peer");
523         this.sessionUp = false;
524         this.adjRibOutListenerSet.values().forEach(AdjRibOutListener::close);
525         this.adjRibOutListenerSet.clear();
526         final FluentFuture<? extends CommitInfo> future;
527         if (!isRestartingGracefully()) {
528             future = terminateConnection();
529         } else {
530             final Set<TablesKey> gracefulTables = getGracefulTables();
531             this.ribWriter.storeStaleRoutes(gracefulTables);
532             future = this.ribWriter.clearTables(Sets.difference(this.tables, gracefulTables));
533             if (isPeerRestarting()) {
534                 this.peerRestartStopwatch = Stopwatch.createStarted();
535                 handleRestartTimer();
536             }
537         }
538         releaseBindingChain();
539
540         closeSession();
541         return future;
542     }
543
544     @Holding("this")
545     @SuppressWarnings("checkstyle:illegalCatch")
546     private void closeSession() {
547         if (this.currentSession != null) {
548             try {
549                 if (isRestartingGracefully()) {
550                     this.currentSession.closeWithoutMessage();
551                 } else {
552                     this.currentSession.close();
553                 }
554             } catch (final Exception e) {
555                 LOG.warn("Error closing session with peer", e);
556             }
557             this.currentSession = null;
558         }
559     }
560
561     private Set<TablesKey> getGracefulTables() {
562         return this.tables.stream()
563                 .filter(this::isGracefulRestartReceived)
564                 .filter(this::isGracefulRestartAdvertized)
565                 .collect(Collectors.toSet());
566     }
567
568     private synchronized FluentFuture<? extends CommitInfo> terminateConnection() {
569         final FluentFuture<? extends CommitInfo> future;
570         if (this.trackerRegistration != null) {
571             this.trackerRegistration.close();
572             this.trackerRegistration = null;
573         }
574         if (this.rpcRegistration != null) {
575             this.rpcRegistration.close();
576         }
577         this.ribWriter.releaseChain();
578
579         if (this.effRibInWriter != null) {
580             this.effRibInWriter.close();
581         }
582         this.tables = ImmutableSet.of();
583         this.addPathTableMaps = Collections.emptyMap();
584         future = removePeer(this.peerPath);
585         resetState();
586
587         return future;
588     }
589
590     /**
591      * If Graceful Restart Timer expires, remove all routes advertised by peer.
592      */
593     private synchronized void handleRestartTimer() {
594         if (!isPeerRestarting()) {
595             return;
596         }
597
598         final long peerRestartTimeNanos = TimeUnit.SECONDS.toNanos(getPeerRestartTime());
599         final long elapsedNanos = this.peerRestartStopwatch.elapsed(TimeUnit.NANOSECONDS);
600         if (elapsedNanos >= peerRestartTimeNanos) {
601             setAfiSafiGracefulRestartState(0, false, false);
602             onSessionTerminated(this.currentSession, new BGPTerminationReason(BGPError.HOLD_TIMER_EXPIRED));
603         }
604
605         currentSession.schedule(this::handleRestartTimer, peerRestartTimeNanos - elapsedNanos, TimeUnit.NANOSECONDS);
606     }
607
608     private synchronized void handleSelectionReferralTimer() {
609         if (!isLocalRestarting()) {
610             return;
611         }
612
613         final long referalTimerNanos = TimeUnit.SECONDS.toNanos(this.currentSelectionDeferralTimerSeconds);
614         final long elapsedNanos = this.peerRestartStopwatch.elapsed(TimeUnit.NANOSECONDS);
615         if (elapsedNanos >= referalTimerNanos) {
616             this.missingEOT.clear();
617             handleGracefulEndOfRib();
618         }
619         currentSession.schedule(this::handleSelectionReferralTimer, referalTimerNanos - elapsedNanos,
620             TimeUnit.NANOSECONDS);
621     }
622
623     private void releaseConnectionGracefully() {
624         if (getPeerRestartTime() > 0) {
625             setRestartingState();
626         }
627         releaseConnection();
628     }
629
630     @SuppressFBWarnings("IS2_INCONSISTENT_SYNC")
631     @Override
632     public SendReceive getSupportedAddPathTables(final TablesKey tableKey) {
633         return this.addPathTableMaps.get(tableKey);
634     }
635
636     @Override
637     public boolean supportsTable(final TablesKey tableKey) {
638         return this.sessionUp && getAfiSafisAdvertized().contains(tableKey);
639     }
640
641     @Override
642     public KeyedInstanceIdentifier<Tables, TablesKey> getRibOutIId(final TablesKey tablesKey) {
643         return this.tablesIId.getUnchecked(tablesKey);
644     }
645
646     @Override
647     public synchronized void onTransactionChainFailed(final DOMTransactionChain chain,
648             final DOMDataTreeTransaction transaction, final Throwable cause) {
649         LOG.error("Transaction domChain failed.", cause);
650         releaseConnection();
651     }
652
653     @Override
654     public synchronized void onTransactionChainFailed(final TransactionChain chain, final Transaction transaction,
655             final Throwable cause) {
656         LOG.error("Transaction domChain failed.", cause);
657         releaseConnection();
658     }
659
660     @Override
661     public synchronized void markUptodate(final TablesKey tablesKey) {
662         this.ribWriter.markTableUptodate(tablesKey);
663     }
664
665     @Override
666     public synchronized BGPSessionState getBGPSessionState() {
667         if (this.currentSession instanceof BGPSessionStateProvider) {
668             return ((BGPSessionStateProvider) this.currentSession).getBGPSessionState();
669         }
670         return null;
671     }
672
673     @Override
674     public synchronized BGPTimersState getBGPTimersState() {
675         if (this.currentSession instanceof BGPSessionStateProvider) {
676             return ((BGPSessionStateProvider) this.currentSession).getBGPTimersState();
677         }
678         return null;
679     }
680
681     @Override
682     public synchronized BGPTransportState getBGPTransportState() {
683         if (this.currentSession instanceof BGPSessionStateProvider) {
684             return ((BGPSessionStateProvider) this.currentSession).getBGPTransportState();
685         }
686         return null;
687     }
688
689     @Override
690     public List<RouteTarget> getMemberships() {
691         return this.rtMemberships;
692     }
693
694     @Override
695     public synchronized ListenableFuture<?> restartGracefully(final long selectionDeferralTimerSeconds) {
696         final Set<TablesKey> tablesToPreserve = getGracefulTables();
697         if (tablesToPreserve == null || tablesToPreserve.isEmpty()) {
698             LOG.info("Peer {} is not capable of graceful restart or have no matching graceful tables.", this.peerId);
699             return Futures.immediateFailedFuture(new UnsupportedOperationException(
700                     "Peer is not capable of graceful restart"));
701         }
702         setGracefulPreferences(true, tablesToPreserve);
703         this.currentSelectionDeferralTimerSeconds = selectionDeferralTimerSeconds;
704         setLocalRestartingState(true);
705         return releaseConnection();
706     }
707
708     @Override
709     boolean supportsLLGR() {
710         return this.llgrSupport;
711     }
712
713     private synchronized void setGracefulPreferences(final boolean localRestarting,
714                                                      final Set<TablesKey> preservedTables) {
715         final Set<TablesKey> gracefulTables = this.tables.stream()
716                 .filter(this::isGracefulRestartAdvertized)
717                 .collect(Collectors.toSet());
718         final BgpParameters bgpParameters = GracefulRestartUtil.getGracefulBgpParameters(
719                 this.bgpPeer.getBgpFixedCapabilities(), gracefulTables, preservedTables,
720                 this.bgpPeer.getGracefulRestartTimer(), localRestarting, Collections.emptySet());
721         final BGPSessionPreferences oldPrefs = this.rib.getDispatcher().getBGPPeerRegistry()
722                 .getPeerPreferences(getNeighborAddress());
723         final BGPSessionPreferences newPrefs = new BGPSessionPreferences(
724                 oldPrefs.getMyAs(),
725                 oldPrefs.getHoldTime(),
726                 oldPrefs.getBgpId(),
727                 oldPrefs.getExpectedRemoteAs(),
728                 Collections.singletonList(bgpParameters),
729                 oldPrefs.getMd5Password());
730         this.rib.getDispatcher().getBGPPeerRegistry()
731                 .updatePeerPreferences(getNeighborAddress(), newPrefs);
732     }
733 }