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