Get rid of JSR305 annotations
[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.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.util.Ipv4Util;
63 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.AsNumber;
64 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev180329.ipv4.prefixes.DestinationIpv4Builder;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev180329.ipv4.prefixes.destination.ipv4.Ipv4Prefixes;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev180329.ipv4.prefixes.destination.ipv4.Ipv4PrefixesBuilder;
68 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;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.Update;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.open.message.BgpParameters;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.path.attributes.Attributes;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.path.attributes.AttributesBuilder;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.update.message.Nlri;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.BgpAddPathTableType;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.BgpTableType;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.RouteRefresh;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.SendReceive;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.mp.capabilities.GracefulRestartCapability;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.mp.capabilities.add.path.capability.AddressFamilies;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpReachNlri;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpReachNlriBuilder;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpUnreachNlri;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpUnreachNlriBuilder;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.mp.reach.nlri.AdvertizedRoutesBuilder;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.mp.unreach.nlri.WithdrawnRoutesBuilder;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.peer.rpc.rev180329.BgpPeerRpcService;
87 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.peer.rpc.rev180329.PeerContext;
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.Registration;
100 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
101 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
102 import org.opendaylight.yangtools.yang.binding.Notification;
103 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
104 import org.slf4j.Logger;
105 import org.slf4j.LoggerFactory;
106
107 /**
108  * Class representing a peer. We have a single instance for each peer, which provides translation from BGP events into
109  * RIB actions.
110  */
111 public class BGPPeer extends AbstractPeer implements BGPSessionListener {
112     private static final Logger LOG = LoggerFactory.getLogger(BGPPeer.class);
113     private static final TablesKey IPV4_UCAST_TABLE_KEY = new TablesKey(Ipv4AddressFamily.class,
114         UnicastSubsequentAddressFamily.class);
115
116     private ImmutableSet<TablesKey> tables = ImmutableSet.of();
117     private final RIB rib;
118     private final Map<TablesKey, AdjRibOutListener> adjRibOutListenerSet = new HashMap<>();
119     private final List<RouteTarget> rtMemberships = new ArrayList<>();
120     private final RpcProviderRegistry rpcRegistry;
121     private final BGPTableTypeRegistryConsumer tableTypeRegistry;
122     private final BgpPeer bgpPeer;
123     private InstanceIdentifier<AdjRibOut> peerRibOutIId;
124     private KeyedInstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib
125             .rev180329.bgp.rib.rib.Peer, PeerKey> peerIId;
126     @GuardedBy("this")
127     private Registration trackerRegistration;
128     private final LoadingCache<TablesKey, KeyedInstanceIdentifier<Tables, TablesKey>> tablesIId
129             = CacheBuilder.newBuilder()
130             .build(new CacheLoader<TablesKey, KeyedInstanceIdentifier<Tables, TablesKey>>() {
131                 @Override
132                 public KeyedInstanceIdentifier<Tables, TablesKey> load(final TablesKey tablesKey) {
133                     return BGPPeer.this.peerRibOutIId.child(Tables.class, tablesKey);
134                 }
135             });
136
137     @GuardedBy("this")
138     private BGPSession currentSession;
139     @GuardedBy("this")
140     private AdjRibInWriter ribWriter;
141     @GuardedBy("this")
142     private EffectiveRibInWriter effRibInWriter;
143     private RoutedRpcRegistration<BgpPeerRpcService> rpcRegistration;
144     private Map<TablesKey, SendReceive> addPathTableMaps = Collections.emptyMap();
145     private YangInstanceIdentifier peerPath;
146     private boolean sessionUp;
147     private boolean llgrSupport;
148     private Stopwatch peerRestartStopwatch;
149     private long currentSelectionDeferralTimerSeconds;
150     private final List<TablesKey> missingEOT = new ArrayList<>();
151
152     public BGPPeer(
153             final BGPTableTypeRegistryConsumer tableTypeRegistry,
154             final IpAddress neighborAddress,
155             final String peerGroupName,
156             final RIB rib,
157             final PeerRole role,
158             final ClusterIdentifier clusterId,
159             final AsNumber localAs,
160             final RpcProviderRegistry rpcRegistry,
161             final Set<TablesKey> afiSafisAdvertized,
162             final Set<TablesKey> afiSafisGracefulAdvertized,
163             final Map<TablesKey, Integer> llGracefulTablesAdvertised,
164             final BgpPeer bgpPeer) {
165         super(rib, Ipv4Util.toStringIP(neighborAddress), peerGroupName, role, clusterId,
166                 localAs, neighborAddress, afiSafisAdvertized, afiSafisGracefulAdvertized, llGracefulTablesAdvertised);
167         this.tableTypeRegistry = requireNonNull(tableTypeRegistry);
168         this.rib = requireNonNull(rib);
169         this.rpcRegistry = rpcRegistry;
170         this.bgpPeer = bgpPeer;
171     }
172
173     private static Attributes nextHopToAttribute(final Attributes attrs, final MpReachNlri mpReach) {
174         if (attrs.getCNextHop() == null && mpReach.getCNextHop() != null) {
175             final AttributesBuilder attributesBuilder = new AttributesBuilder(attrs);
176             attributesBuilder.setCNextHop(mpReach.getCNextHop());
177             return attributesBuilder.build();
178         }
179         return attrs;
180     }
181
182     /**
183      * Creates MPReach for the prefixes to be handled in the same way as linkstate routes.
184      *
185      * @param message Update message containing prefixes in NLRI
186      * @return MpReachNlri with prefixes from the nlri field
187      */
188     private static MpReachNlri prefixesToMpReach(final Update message) {
189         final List<Ipv4Prefixes> prefixes = message.getNlri().stream()
190                 .map(n -> new Ipv4PrefixesBuilder().setPrefix(n.getPrefix()).setPathId(n.getPathId()).build())
191                 .collect(Collectors.toList());
192         final MpReachNlriBuilder b = new MpReachNlriBuilder().setAfi(Ipv4AddressFamily.class).setSafi(
193                 UnicastSubsequentAddressFamily.class).setAdvertizedRoutes(
194                 new AdvertizedRoutesBuilder().setDestinationType(
195                         new DestinationIpv4CaseBuilder().setDestinationIpv4(
196                                 new DestinationIpv4Builder().setIpv4Prefixes(prefixes).build()).build()).build());
197         if (message.getAttributes() != null) {
198             b.setCNextHop(message.getAttributes().getCNextHop());
199         }
200         return b.build();
201     }
202
203     /**
204      * Create MPUnreach for the prefixes to be handled in the same way as linkstate routes.
205      *
206      * @param message            Update message containing withdrawn routes
207      * @param isAnyNlriAnnounced isAnyNlriAnnounced
208      * @return MpUnreachNlri with prefixes from the withdrawn routes field
209      */
210     private static MpUnreachNlri prefixesToMpUnreach(final Update message, final boolean isAnyNlriAnnounced) {
211         final List<Ipv4Prefixes> prefixes = new ArrayList<>();
212         message.getWithdrawnRoutes().forEach(w -> {
213
214             Optional<Nlri> nlriAnounced = Optional.empty();
215             if (isAnyNlriAnnounced) {
216                 nlriAnounced = message.getNlri().stream().filter(n -> Objects.equal(n.getPrefix(), w.getPrefix())
217                         && Objects.equal(n.getPathId(), w.getPathId()))
218                         .findAny();
219             }
220             if (!nlriAnounced.isPresent()) {
221                 prefixes.add(new Ipv4PrefixesBuilder().setPrefix(w.getPrefix()).setPathId(w.getPathId()).build());
222             }
223         });
224         return new MpUnreachNlriBuilder().setAfi(Ipv4AddressFamily.class).setSafi(UnicastSubsequentAddressFamily.class)
225                 .setWithdrawnRoutes(new WithdrawnRoutesBuilder().setDestinationType(new org.opendaylight.yang.gen.v1
226                         .urn.opendaylight.params.xml.ns.yang.bgp.inet.rev180329.update.attributes.mp.unreach.nlri
227                         .withdrawn.routes.destination.type.DestinationIpv4CaseBuilder().setDestinationIpv4(
228                         new DestinationIpv4Builder().setIpv4Prefixes(prefixes).build()).build()).build()).build();
229     }
230
231     private static Map<TablesKey, SendReceive> mapTableTypesFamilies(final List<AddressFamilies> addPathTablesType) {
232         return ImmutableMap.copyOf(addPathTablesType.stream().collect(Collectors.toMap(af -> new TablesKey(af.getAfi(),
233                         af.getSafi()), BgpAddPathTableType::getSendReceive)));
234     }
235
236     public synchronized void instantiateServiceInstance() {
237         this.ribWriter = AdjRibInWriter.create(this.rib.getYangRibId(), this.peerRole, this);
238         setActive(true);
239     }
240
241     @Override
242     public synchronized FluentFuture<? extends CommitInfo> close() {
243         final FluentFuture<? extends CommitInfo> future = releaseConnection();
244         closeDomChain();
245         setActive(false);
246         return future;
247     }
248
249     @Override
250     public void onMessage(final BGPSession session, final Notification msg) throws BGPDocumentedException {
251         if (msg instanceof Update) {
252             onUpdateMessage((Update) msg);
253         } else if (msg instanceof RouteRefresh) {
254             onRouteRefreshMessage((RouteRefresh) msg);
255         } else {
256             LOG.info("Ignoring unhandled message class {}", msg.getClass());
257         }
258     }
259
260     private void onRouteRefreshMessage(final RouteRefresh message) {
261         final Class<? extends AddressFamily> rrAfi = message.getAfi();
262         final Class<? extends SubsequentAddressFamily> rrSafi = message.getSafi();
263
264         final TablesKey key = new TablesKey(rrAfi, rrSafi);
265         synchronized (this) {
266             final AdjRibOutListener listener = this.adjRibOutListenerSet.remove(key);
267             if (listener != null) {
268                 listener.close();
269                 createAdjRibOutListener(key, listener.isMpSupported());
270             } else {
271                 LOG.info("Ignoring RouteRefresh message. Afi/Safi is not supported: {}, {}.", rrAfi, rrSafi);
272             }
273         }
274     }
275
276     /**
277      * Check for presence of well known mandatory attribute LOCAL_PREF in Update message.
278      *
279      * @param message Update message
280      */
281     private void checkMandatoryAttributesPresence(final Update message) throws BGPDocumentedException {
282         if (MessageUtil.isAnyNlriPresent(message)) {
283             final Attributes attrs = message.getAttributes();
284             if (this.peerRole == PeerRole.Ibgp && (attrs == null || attrs.getLocalPref() == null)) {
285                 throw new BGPDocumentedException(BGPError.MANDATORY_ATTR_MISSING_MSG + "LOCAL_PREF",
286                         BGPError.WELL_KNOWN_ATTR_MISSING,
287                         new byte[]{LocalPreferenceAttributeParser.TYPE});
288             }
289         }
290     }
291
292     /**
293      * Process Update message received.
294      * Calls {@link #checkMandatoryAttributesPresence(Update)} to check for presence of mandatory attributes.
295      *
296      * @param message Update message
297      */
298     private synchronized void onUpdateMessage(final Update message) throws BGPDocumentedException {
299         checkMandatoryAttributesPresence(message);
300
301         // update AdjRibs
302         final Attributes attrs = message.getAttributes();
303         MpReachNlri mpReach;
304         final boolean isAnyNlriAnnounced = message.getNlri() != null;
305         if (isAnyNlriAnnounced) {
306             mpReach = prefixesToMpReach(message);
307         } else {
308             mpReach = MessageUtil.getMpReachNlri(attrs);
309         }
310         if (mpReach != null) {
311             this.ribWriter.updateRoutes(mpReach, nextHopToAttribute(attrs, mpReach));
312         }
313         final MpUnreachNlri mpUnreach;
314         if (message.getWithdrawnRoutes() != null) {
315             mpUnreach = prefixesToMpUnreach(message, isAnyNlriAnnounced);
316         } else {
317             mpUnreach = MessageUtil.getMpUnreachNlri(attrs);
318         }
319         final boolean endOfRib = BgpPeerUtil.isEndOfRib(message);
320         if (mpUnreach != null) {
321             if (endOfRib) {
322                 final TablesKey tablesKey = new TablesKey(mpUnreach.getAfi(), mpUnreach.getSafi());
323                 this.ribWriter.removeStaleRoutes(tablesKey);
324                 this.missingEOT.remove(tablesKey);
325                 handleGracefulEndOfRib();
326             } else {
327                 this.ribWriter.removeRoutes(mpUnreach);
328             }
329         } else if (endOfRib) {
330             this.ribWriter.removeStaleRoutes(IPV4_UCAST_TABLE_KEY);
331             this.missingEOT.remove(IPV4_UCAST_TABLE_KEY);
332             handleGracefulEndOfRib();
333         }
334     }
335
336     @Holding("this")
337     private void handleGracefulEndOfRib() {
338         if (isLocalRestarting()) {
339             if (this.missingEOT.isEmpty()) {
340                 createEffRibInWriter();
341                 this.effRibInWriter.init();
342                 registerPrefixesCounters(this.effRibInWriter, this.effRibInWriter);
343                 for (final TablesKey key : getAfiSafisAdvertized()) {
344                     createAdjRibOutListener(key, true);
345                 }
346                 setLocalRestartingState(false);
347                 setGracefulPreferences(false, Collections.emptySet());
348             }
349         }
350     }
351
352     @Override
353     public synchronized void onSessionUp(final BGPSession session) {
354         this.currentSession = session;
355         this.sessionUp = true;
356         this.bindingChain = this.rib.createPeerChain(this);
357         if (this.currentSession instanceof BGPSessionStateProvider) {
358             ((BGPSessionStateProvider) this.currentSession).registerMessagesCounter(this);
359         }
360         final GracefulRestartCapability advertisedGracefulRestartCapability =
361                 session.getAdvertisedGracefulRestartCapability();
362         final List<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.mp
363                 .capabilities.graceful.restart.capability.Tables> advertisedTables =
364                     advertisedGracefulRestartCapability.getTables();
365         final List<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.mp
366                 .capabilities.ll.graceful.restart.capability.Tables> advertisedLLTables =
367                     session.getAdvertisedLlGracefulRestartCapability().getTables();
368
369         final List<AddressFamilies> addPathTablesType = session.getAdvertisedAddPathTableTypes();
370         final Set<BgpTableType> advertizedTableTypes = session.getAdvertisedTableTypes();
371         LOG.info("Session with peer {} went up with tables {} and Add Path tables {}", this.name,
372                 advertizedTableTypes, addPathTablesType);
373         final Set<TablesKey> setTables = advertizedTableTypes.stream().map(t -> new TablesKey(t.getAfi(), t.getSafi()))
374                 .collect(Collectors.toSet());
375         this.tables = ImmutableSet.copyOf(setTables);
376
377         this.addPathTableMaps = mapTableTypesFamilies(addPathTablesType);
378         final boolean restartingLocally = isLocalRestarting();
379
380         if (!isRestartingGracefully()) {
381             this.rawIdentifier = InetAddresses.forString(session.getBgpId().getValue()).getAddress();
382             this.peerId = RouterIds.createPeerId(session.getBgpId());
383             this.peerIId = getInstanceIdentifier().child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns
384                             .yang.bgp.rib.rev180329.bgp.rib.rib.Peer.class, new PeerKey(this.peerId));
385             this.peerPath = createPeerPath();
386             this.peerRibOutIId = peerIId.child(AdjRibOut.class);
387             this.trackerRegistration = this.rib.getPeerTracker().registerPeer(this);
388             createEffRibInWriter();
389             registerPrefixesCounters(this.effRibInWriter, this.effRibInWriter);
390
391             this.effRibInWriter.init();
392             this.ribWriter = this.ribWriter.transform(this.peerId, this.peerPath, this.rib.getRibSupportContext(),
393                     this.tables, this.addPathTableMaps);
394
395             if (this.rpcRegistry != null) {
396                 this.rpcRegistration = this.rpcRegistry.addRoutedRpcImplementation(BgpPeerRpcService.class,
397                         new BgpPeerRpc(this, session, this.tables));
398                 final KeyedInstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib
399                         .rev180329.bgp.rib.rib.Peer, PeerKey> path = this.rib.getInstanceIdentifier()
400                         .child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp
401                             .rib.rib.Peer.class, new PeerKey(this.peerId));
402                 this.rpcRegistration.registerPath(PeerContext.class, path);
403             }
404         } else {
405             final Set<TablesKey> forwardingTables;
406             if (advertisedTables == null) {
407                 forwardingTables = Collections.emptySet();
408             } else {
409                 forwardingTables = advertisedTables.stream()
410                         .filter(table -> table.getAfiFlags() != null)
411                         .filter(table -> table.getAfiFlags().isForwardingState())
412                         .map(table -> new TablesKey(table.getAfi(), table.getSafi()))
413                         .collect(Collectors.toSet());
414             }
415             this.ribWriter.clearTables(Sets.difference(this.tables, forwardingTables));
416             if (restartingLocally) {
417                 this.effRibInWriter.close();
418                 this.peerRestartStopwatch = Stopwatch.createStarted();
419                 handleSelectionReferralTimer();
420                 this.missingEOT.addAll(this.tables);
421             }
422         }
423         if (advertisedTables == null || advertisedTables.isEmpty()) {
424             setAdvertizedGracefulRestartTableTypes(Collections.emptyList());
425         } else {
426             setAdvertizedGracefulRestartTableTypes(advertisedTables.stream()
427                     .map(t -> new TablesKey(t.getAfi(), t.getSafi())).collect(Collectors.toList()));
428         }
429         setAfiSafiGracefulRestartState(advertisedGracefulRestartCapability.getRestartTime(), false, 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().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 TransactionChain<?, ?> chain,
653             final AsyncTransaction<?, ?> transaction, final Throwable cause) {
654         LOG.error("Transaction domChain failed.", cause);
655         releaseConnection();
656     }
657
658     @Override
659     public synchronized void markUptodate(final TablesKey tablesKey) {
660         this.ribWriter.markTableUptodate(tablesKey);
661     }
662
663     @Override
664     public synchronized BGPSessionState getBGPSessionState() {
665         if (this.currentSession instanceof BGPSessionStateProvider) {
666             return ((BGPSessionStateProvider) this.currentSession).getBGPSessionState();
667         }
668         return null;
669     }
670
671     @Override
672     public synchronized BGPTimersState getBGPTimersState() {
673         if (this.currentSession instanceof BGPSessionStateProvider) {
674             return ((BGPSessionStateProvider) this.currentSession).getBGPTimersState();
675         }
676         return null;
677     }
678
679     @Override
680     public synchronized BGPTransportState getBGPTransportState() {
681         if (this.currentSession instanceof BGPSessionStateProvider) {
682             return ((BGPSessionStateProvider) this.currentSession).getBGPTransportState();
683         }
684         return null;
685     }
686
687     @Override
688     public List<RouteTarget> getMemberships() {
689         return this.rtMemberships;
690     }
691
692     @Override
693     public synchronized ListenableFuture<?> restartGracefully(final long selectionDeferralTimerSeconds) {
694         final Set<TablesKey> tablesToPreserve = getGracefulTables();
695         if (tablesToPreserve == null || tablesToPreserve.isEmpty()) {
696             LOG.info("Peer {} is not capable of graceful restart or have no matching graceful tables.", this.peerId);
697             return Futures.immediateFailedFuture(new UnsupportedOperationException(
698                     "Peer is not capable of graceful restart"));
699         }
700         setGracefulPreferences(true, tablesToPreserve);
701         this.currentSelectionDeferralTimerSeconds = selectionDeferralTimerSeconds;
702         setLocalRestartingState(true);
703         return releaseConnection();
704     }
705
706     @Override
707     boolean supportsLLGR() {
708         return this.llgrSupport;
709     }
710
711     private synchronized void setGracefulPreferences(final boolean localRestarting,
712                                                      final Set<TablesKey> preservedTables) {
713         final Set<TablesKey> gracefulTables = this.tables.stream()
714                 .filter(this::isGracefulRestartAdvertized)
715                 .collect(Collectors.toSet());
716         final BgpParameters bgpParameters = GracefulRestartUtil.getGracefulBgpParameters(
717                 this.bgpPeer.getBgpFixedCapabilities(), gracefulTables, preservedTables,
718                 this.bgpPeer.getGracefulRestartTimer(), localRestarting, Collections.emptySet());
719         final BGPSessionPreferences oldPrefs = this.rib.getDispatcher().getBGPPeerRegistry()
720                 .getPeerPreferences(getNeighborAddress());
721         final BGPSessionPreferences newPrefs = new BGPSessionPreferences(
722                 oldPrefs.getMyAs(),
723                 oldPrefs.getHoldTime(),
724                 oldPrefs.getBgpId(),
725                 oldPrefs.getExpectedRemoteAs(),
726                 Collections.singletonList(bgpParameters),
727                 oldPrefs.getMd5Password());
728         this.rib.getDispatcher().getBGPPeerRegistry()
729                 .updatePeerPreferences(getNeighborAddress(), newPrefs);
730     }
731 }