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