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