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