Fix more rib-impl checkstyle
[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         final AdjRibOutListener listener = this.adjRibOutListenerSet.get(key);
264         if (listener != null) {
265             listener.close();
266             this.adjRibOutListenerSet.remove(key);
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      * Check for presence of well known mandatory attribute LOCAL_PREF in Update message.
275      *
276      * @param message Update message
277      */
278     private void checkMandatoryAttributesPresence(final Update message) throws BGPDocumentedException {
279         if (MessageUtil.isAnyNlriPresent(message)) {
280             final Attributes attrs = message.getAttributes();
281             if (this.peerRole == PeerRole.Ibgp && (attrs == null || attrs.getLocalPref() == null)) {
282                 throw new BGPDocumentedException(BGPError.MANDATORY_ATTR_MISSING_MSG + "LOCAL_PREF",
283                         BGPError.WELL_KNOWN_ATTR_MISSING,
284                         new byte[]{LocalPreferenceAttributeParser.TYPE});
285             }
286         }
287     }
288
289     /**
290      * Process Update message received.
291      * Calls {@link #checkMandatoryAttributesPresence(Update)} to check for presence of mandatory attributes.
292      *
293      * @param message Update message
294      */
295     private synchronized void onUpdateMessage(final Update message) throws BGPDocumentedException {
296         checkMandatoryAttributesPresence(message);
297
298         // update AdjRibs
299         final Attributes attrs = message.getAttributes();
300         MpReachNlri mpReach;
301         final boolean isAnyNlriAnnounced = message.getNlri() != null;
302         if (isAnyNlriAnnounced) {
303             mpReach = prefixesToMpReach(message);
304         } else {
305             mpReach = MessageUtil.getMpReachNlri(attrs);
306         }
307         if (mpReach != null) {
308             this.ribWriter.updateRoutes(mpReach, nextHopToAttribute(attrs, mpReach));
309         }
310         MpUnreachNlri mpUnreach;
311         if (message.getWithdrawnRoutes() != null) {
312             mpUnreach = prefixesToMpUnreach(message, isAnyNlriAnnounced);
313         } else {
314             mpUnreach = MessageUtil.getMpUnreachNlri(attrs);
315         }
316         final boolean endOfRib = BgpPeerUtil.isEndOfRib(message);
317         if (mpUnreach != null) {
318             if (endOfRib) {
319                 final TablesKey tablesKey = new TablesKey(mpUnreach.getAfi(), mpUnreach.getSafi());
320                 this.ribWriter.removeStaleRoutes(tablesKey);
321                 this.missingEOT.remove(tablesKey);
322                 handleGracefulEndOfRib();
323             } else {
324                 this.ribWriter.removeRoutes(mpUnreach);
325             }
326         } else if (endOfRib) {
327             this.ribWriter.removeStaleRoutes(IPV4_UCAST_TABLE_KEY);
328             this.missingEOT.remove(IPV4_UCAST_TABLE_KEY);
329             handleGracefulEndOfRib();
330         }
331     }
332
333     private synchronized void handleGracefulEndOfRib() {
334         if (isLocalRestarting()) {
335             if (this.missingEOT.isEmpty()) {
336                 createEffRibInWriter();
337                 this.effRibInWriter.init();
338                 registerPrefixesCounters(this.effRibInWriter, this.effRibInWriter);
339                 for (final TablesKey key : this.tables) {
340                     createAdjRibOutListener(key, true);
341                 }
342                 setLocalRestartingState(false);
343                 setGracefulPreferences(false, Collections.emptySet());
344             }
345         }
346     }
347
348     @Override
349     public synchronized void onSessionUp(final BGPSession session) {
350         this.session = session;
351         this.sessionUp = true;
352         this.bindingChain = this.rib.createPeerChain(this);
353         if (this.session instanceof BGPSessionStateProvider) {
354             ((BGPSessionStateProvider) this.session).registerMessagesCounter(this);
355         }
356         final GracefulRestartCapability advertisedGracefulRestartCapability =
357                 session.getAdvertisedGracefulRestartCapability();
358         final List<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.mp
359                 .capabilities.graceful.restart.capability.Tables> advertisedTables =
360                     advertisedGracefulRestartCapability.getTables();
361         final List<AddressFamilies> addPathTablesType = session.getAdvertisedAddPathTableTypes();
362         final Set<BgpTableType> advertizedTableTypes = session.getAdvertisedTableTypes();
363         LOG.info("Session with peer {} went up with tables {} and Add Path tables {}", this.name,
364                 advertizedTableTypes, addPathTablesType);
365         final Set<TablesKey> setTables = advertizedTableTypes.stream().map(t -> new TablesKey(t.getAfi(), t.getSafi()))
366                 .collect(Collectors.toSet());
367         this.tables = ImmutableSet.copyOf(setTables);
368         this.addPathTableMaps = mapTableTypesFamilies(addPathTablesType);
369         final boolean restartingLocally = isLocalRestarting();
370
371         if (!isRestartingGracefully()) {
372             this.rawIdentifier = InetAddresses.forString(session.getBgpId().getValue()).getAddress();
373             this.peerId = RouterIds.createPeerId(session.getBgpId());
374             this.peerIId = getInstanceIdentifier().child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns
375                             .yang.bgp.rib.rev180329.bgp.rib.rib.Peer.class, new PeerKey(this.peerId));
376             this.peerPath = createPeerPath();
377             this.peerRibOutIId = peerIId.child(AdjRibOut.class);
378             this.trackerRegistration = this.rib.getPeerTracker().registerPeer(this);
379             createEffRibInWriter();
380             registerPrefixesCounters(this.effRibInWriter, this.effRibInWriter);
381
382             this.effRibInWriter.init();
383             this.ribWriter = this.ribWriter.transform(this.peerId, this.peerPath, this.rib.getRibSupportContext(),
384                     this.tables, this.addPathTableMaps);
385
386             if (this.rpcRegistry != null) {
387                 this.rpcRegistration = this.rpcRegistry.addRoutedRpcImplementation(BgpPeerRpcService.class,
388                         new BgpPeerRpc(this, session, this.tables));
389                 final KeyedInstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib
390                         .rev180329.bgp.rib.rib.Peer, PeerKey> path = this.rib.getInstanceIdentifier()
391                         .child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp
392                             .rib.rib.Peer.class, new PeerKey(this.peerId));
393                 this.rpcRegistration.registerPath(PeerContext.class, path);
394             }
395         } else {
396             final Set<TablesKey> forwardingTables;
397             if (advertisedTables == null) {
398                 forwardingTables = Collections.emptySet();
399             } else {
400                 forwardingTables = advertisedTables.stream()
401                         .filter(table -> table.getAfiFlags() != null)
402                         .filter(table -> table.getAfiFlags().isForwardingState())
403                         .map(table -> new TablesKey(table.getAfi(), table.getSafi()))
404                         .collect(Collectors.toSet());
405             }
406             this.ribWriter.clearTables(Sets.difference(this.tables, forwardingTables));
407             if (restartingLocally) {
408                 this.effRibInWriter.close();
409                 this.peerRestartStopwatch = Stopwatch.createStarted();
410                 handleSelectionReferralTimer();
411                 this.missingEOT.addAll(this.tables);
412             }
413         }
414         if (advertisedTables == null || advertisedTables.isEmpty()) {
415             setAdvertizedGracefulRestartTableTypes(Collections.emptyList());
416         } else {
417             setAdvertizedGracefulRestartTableTypes(advertisedTables.stream()
418                     .map(t -> new TablesKey(t.getAfi(), t.getSafi())).collect(Collectors.toList()));
419         }
420         final int restartTime = advertisedGracefulRestartCapability.getRestartTime();
421         setAfiSafiGracefulRestartState(restartTime, false, restartingLocally);
422         if (!restartingLocally) {
423             addBgp4Support();
424             for (final TablesKey key : this.tables) {
425                 createAdjRibOutListener(key, true);
426             }
427         }
428
429         // SpotBugs does not grok Optional.ifPresent() and thinks we are using unsynchronized access
430         final Optional<RevisedErrorHandlingSupport> errorHandling = this.bgpPeer.getErrorHandling();
431         if (errorHandling.isPresent()) {
432             this.session.addDecoderConstraint(RevisedErrorHandlingSupport.class, errorHandling.get());
433         }
434     }
435
436     private boolean isRestartingGracefully() {
437         return isLocalRestarting() || isPeerRestarting();
438     }
439
440     private synchronized void createEffRibInWriter() {
441         this.effRibInWriter = new EffectiveRibInWriter(this, this.rib,
442                 this.rib.createPeerChain(this),
443                 this.peerIId, this.tables, this.tableTypeRegistry,
444                 this.rtMemberships,
445                 this.rtCache);
446     }
447
448     //try to add a support for old-school BGP-4, if peer did not advertise IPv4-Unicast MP capability
449     private synchronized void addBgp4Support() {
450         if (!this.tables.contains(IPV4_UCAST_TABLE_KEY)) {
451             final HashSet<TablesKey> newSet = new HashSet<>(this.tables);
452             newSet.add(IPV4_UCAST_TABLE_KEY);
453             this.tables = ImmutableSet.copyOf(newSet);
454             createAdjRibOutListener(IPV4_UCAST_TABLE_KEY, false);
455         }
456     }
457
458     private synchronized void createAdjRibOutListener(final TablesKey key,
459                                                       final boolean mpSupport) {
460         final RIBSupport<?, ?, ?, ?> ribSupport = this.rib.getRibSupportContext().getRIBSupport(key);
461
462         // not particularly nice
463         if (ribSupport != null && this.session instanceof BGPSessionImpl) {
464             final ChannelOutputLimiter limiter = ((BGPSessionImpl) this.session).getLimiter();
465             final AdjRibOutListener adjRibOut = AdjRibOutListener.create(this.peerId, key,
466                     this.rib.getYangRibId(), this.rib.getCodecsRegistry(), ribSupport,
467                     this.rib.getService(), limiter, mpSupport);
468             this.adjRibOutListenerSet.put(key, adjRibOut);
469             registerPrefixesSentCounter(key, adjRibOut);
470         }
471     }
472
473     @Override
474     public synchronized void onSessionDown(final BGPSession session, final Exception e) {
475         if (e.getMessage().equals(BGPSessionImpl.END_OF_INPUT)) {
476             LOG.info("Session with peer {} went down", this.name);
477         } else {
478             LOG.info("Session with peer {} went down", this.name, e);
479         }
480         releaseConnectionGracefully();
481     }
482
483     @Override
484     public synchronized void onSessionTerminated(final BGPSession session, final BGPTerminationReason cause) {
485         LOG.info("Session with peer {} terminated: {}", this.name, cause);
486         releaseConnectionGracefully();
487     }
488
489     @Override
490     public String toString() {
491         return MoreObjects.toStringHelper(this).add("name", this.name).add("tables", this.tables).toString();
492     }
493
494     @Override
495     public synchronized FluentFuture<? extends CommitInfo> releaseConnection() {
496         LOG.info("Closing session with peer");
497         this.sessionUp = false;
498         this.adjRibOutListenerSet.values().forEach(AdjRibOutListener::close);
499         this.adjRibOutListenerSet.clear();
500         final FluentFuture<? extends CommitInfo> future;
501         if (!isRestartingGracefully()) {
502             future = terminateConnection();
503         } else {
504             final Set<TablesKey> gracefulTables = getGracefulTables();
505             this.ribWriter.storeStaleRoutes(gracefulTables);
506             future = this.ribWriter.clearTables(Sets.difference(this.tables, gracefulTables));
507             if (isPeerRestarting()) {
508                 this.peerRestartStopwatch = Stopwatch.createStarted();
509                 handleRestartTimer();
510             }
511         }
512         releaseBindingChain();
513
514         if (this.session != null) {
515             try {
516                 if (isRestartingGracefully()) {
517                     this.session.closeWithoutMessage();
518                 } else {
519                     this.session.close();
520                 }
521             } catch (final Exception e) {
522                 LOG.warn("Error closing session with peer", e);
523             }
524             this.session = null;
525         }
526         return future;
527     }
528
529     private Set<TablesKey> getGracefulTables() {
530         return this.tables.stream()
531                 .filter(this::isGracefulRestartReceived)
532                 .filter(this::isGracefulRestartAdvertized)
533                 .collect(Collectors.toSet());
534     }
535
536     private synchronized FluentFuture<? extends CommitInfo> terminateConnection() {
537         final FluentFuture<? extends CommitInfo> future;
538         if (this.trackerRegistration != null) {
539             this.trackerRegistration.close();
540             this.trackerRegistration = null;
541         }
542         if (this.rpcRegistration != null) {
543             this.rpcRegistration.close();
544         }
545         this.ribWriter.releaseChain();
546
547         if (this.effRibInWriter != null) {
548             this.effRibInWriter.close();
549         }
550         this.tables = Collections.emptySet();
551         this.addPathTableMaps = Collections.emptyMap();
552         future = removePeer(this.peerPath);
553         resetState();
554
555         return future;
556     }
557
558     /**
559      * If Graceful Restart Timer expires, remove all routes advertised by peer.
560      */
561     private synchronized void handleRestartTimer() {
562         if (!isPeerRestarting()) {
563             return;
564         }
565
566         final long peerRestartTimeNanos = TimeUnit.SECONDS.toNanos(getPeerRestartTime());
567         final long elapsedNanos = this.peerRestartStopwatch.elapsed(TimeUnit.NANOSECONDS);
568         if (elapsedNanos >= peerRestartTimeNanos) {
569             setAfiSafiGracefulRestartState(0, false, false);
570             onSessionTerminated(this.session, new BGPTerminationReason(BGPError.HOLD_TIMER_EXPIRED));
571         }
572         new ScheduledThreadPoolExecutor(1)
573                 .schedule(this::handleRestartTimer, peerRestartTimeNanos - elapsedNanos, TimeUnit.NANOSECONDS);
574     }
575
576     private synchronized void handleSelectionReferralTimer() {
577         if (!isLocalRestarting()) {
578             return;
579         }
580
581         final long referalTimerNanos = TimeUnit.SECONDS.toNanos(this.selectionDeferralTimerSeconds);
582         final long elapsedNanos = this.peerRestartStopwatch.elapsed(TimeUnit.NANOSECONDS);
583         if (elapsedNanos >= referalTimerNanos) {
584             this.missingEOT.clear();
585             handleGracefulEndOfRib();
586         }
587         new ScheduledThreadPoolExecutor(1)
588                 .schedule(this::handleSelectionReferralTimer, referalTimerNanos - elapsedNanos, TimeUnit.NANOSECONDS);
589     }
590
591     private void releaseConnectionGracefully() {
592         if (getPeerRestartTime() > 0) {
593             setRestartingState();
594         }
595         releaseConnection();
596     }
597
598     @SuppressFBWarnings("IS2_INCONSISTENT_SYNC")
599     @Override
600     public SendReceive getSupportedAddPathTables(final TablesKey tableKey) {
601         return this.addPathTableMaps.get(tableKey);
602     }
603
604     @Override
605     public boolean supportsTable(final TablesKey tableKey) {
606         return this.tables.contains(tableKey) && this.sessionUp;
607     }
608
609     @Override
610     public KeyedInstanceIdentifier<Tables, TablesKey> getRibOutIId(final TablesKey tablesKey) {
611         return this.tablesIId.getUnchecked(tablesKey);
612     }
613
614     @Override
615     public synchronized void onTransactionChainFailed(final TransactionChain<?, ?> chain,
616             final AsyncTransaction<?, ?> transaction, final Throwable cause) {
617         LOG.error("Transaction domChain failed.", cause);
618         releaseConnection();
619     }
620
621     @Override
622     public synchronized void markUptodate(final TablesKey tablesKey) {
623         this.ribWriter.markTableUptodate(tablesKey);
624     }
625
626     @Override
627     public synchronized BGPSessionState getBGPSessionState() {
628         if (this.session instanceof BGPSessionStateProvider) {
629             return ((BGPSessionStateProvider) this.session).getBGPSessionState();
630         }
631         return null;
632     }
633
634     @Override
635     public synchronized BGPTimersState getBGPTimersState() {
636         if (this.session instanceof BGPSessionStateProvider) {
637             return ((BGPSessionStateProvider) this.session).getBGPTimersState();
638         }
639         return null;
640     }
641
642     @Override
643     public synchronized BGPTransportState getBGPTransportState() {
644         if (this.session instanceof BGPSessionStateProvider) {
645             return ((BGPSessionStateProvider) this.session).getBGPTransportState();
646         }
647         return null;
648     }
649
650     @Override
651     public List<RouteTarget> getMemberships() {
652         return this.rtMemberships;
653     }
654
655     @Override
656     public synchronized ListenableFuture<?> restartGracefully(final long selectionDeferralTimerSeconds) {
657         final Set<TablesKey> tablesToPreserve = getGracefulTables();
658         if (tablesToPreserve == null || tablesToPreserve.isEmpty()) {
659             LOG.info("Peer {} is not capable of graceful restart or have no matching graceful tables.", this.peerId);
660             return Futures.immediateFailedFuture(new UnsupportedOperationException(
661                     "Peer is not capable of graceful restart"));
662         }
663         setGracefulPreferences(true, tablesToPreserve);
664         this.selectionDeferralTimerSeconds = selectionDeferralTimerSeconds;
665         setLocalRestartingState(true);
666         return releaseConnection();
667     }
668
669     private synchronized void setGracefulPreferences(final boolean localRestarting,
670                                                      final Set<TablesKey> preservedTables) {
671         final Set<TablesKey> gracefulTables = this.tables.stream()
672                 .filter(this::isGracefulRestartAdvertized)
673                 .collect(Collectors.toSet());
674         final BgpParameters bgpParameters = GracefulRestartUtil.getGracefulBgpParameters(
675                 this.bgpPeer.getBgpFixedCapabilities(), gracefulTables, preservedTables,
676                 this.bgpPeer.getGracefulRestartTimer(), localRestarting, Collections.emptySet());
677         final BGPSessionPreferences oldPrefs = this.rib.getDispatcher().getBGPPeerRegistry()
678                 .getPeerPreferences(getNeighborAddress());
679         final BGPSessionPreferences newPrefs = new BGPSessionPreferences(
680                 oldPrefs.getMyAs(),
681                 oldPrefs.getHoldTime(),
682                 oldPrefs.getBgpId(),
683                 oldPrefs.getExpectedRemoteAs(),
684                 Collections.singletonList(bgpParameters),
685                 oldPrefs.getMd5Password());
686         this.rib.getDispatcher().getBGPPeerRegistry()
687                 .updatePeerPreferences(getNeighborAddress(), newPrefs);
688     }
689 }