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