Propagate LLGR configuration
[bgpcep.git] / bgp / rib-impl / src / main / java / org / opendaylight / protocol / bgp / rib / impl / BGPPeer.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.protocol.bgp.rib.impl;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.base.MoreObjects;
13 import com.google.common.base.Objects;
14 import com.google.common.base.Stopwatch;
15 import com.google.common.cache.CacheBuilder;
16 import com.google.common.cache.CacheLoader;
17 import com.google.common.cache.LoadingCache;
18 import com.google.common.collect.ImmutableMap;
19 import com.google.common.collect.ImmutableSet;
20 import com.google.common.collect.Sets;
21 import com.google.common.net.InetAddresses;
22 import com.google.common.util.concurrent.FluentFuture;
23 import com.google.common.util.concurrent.Futures;
24 import com.google.common.util.concurrent.ListenableFuture;
25 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
26 import java.util.ArrayList;
27 import java.util.Collections;
28 import java.util.HashMap;
29 import java.util.HashSet;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Optional;
33 import java.util.Set;
34 import java.util.concurrent.ScheduledThreadPoolExecutor;
35 import java.util.concurrent.TimeUnit;
36 import java.util.stream.Collectors;
37 import javax.annotation.concurrent.GuardedBy;
38 import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
39 import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
40 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RoutedRpcRegistration;
41 import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
42 import org.opendaylight.mdsal.common.api.CommitInfo;
43 import org.opendaylight.protocol.bgp.openconfig.spi.BGPTableTypeRegistryConsumer;
44 import org.opendaylight.protocol.bgp.parser.BGPDocumentedException;
45 import org.opendaylight.protocol.bgp.parser.BGPError;
46 import org.opendaylight.protocol.bgp.parser.impl.message.update.LocalPreferenceAttributeParser;
47 import org.opendaylight.protocol.bgp.parser.spi.MessageUtil;
48 import org.opendaylight.protocol.bgp.parser.spi.RevisedErrorHandlingSupport;
49 import org.opendaylight.protocol.bgp.rib.impl.config.BgpPeer;
50 import org.opendaylight.protocol.bgp.rib.impl.config.GracefulRestartUtil;
51 import org.opendaylight.protocol.bgp.rib.impl.spi.BGPSessionPreferences;
52 import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
53 import org.opendaylight.protocol.bgp.rib.impl.state.BGPSessionStateProvider;
54 import org.opendaylight.protocol.bgp.rib.spi.BGPSession;
55 import org.opendaylight.protocol.bgp.rib.spi.BGPSessionListener;
56 import org.opendaylight.protocol.bgp.rib.spi.BGPTerminationReason;
57 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
58 import org.opendaylight.protocol.bgp.rib.spi.RouterIds;
59 import org.opendaylight.protocol.bgp.rib.spi.state.BGPSessionState;
60 import org.opendaylight.protocol.bgp.rib.spi.state.BGPTimersState;
61 import org.opendaylight.protocol.bgp.rib.spi.state.BGPTransportState;
62 import org.opendaylight.protocol.concepts.AbstractRegistration;
63 import org.opendaylight.protocol.util.Ipv4Util;
64 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.AsNumber;
65 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev180329.ipv4.prefixes.DestinationIpv4Builder;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev180329.ipv4.prefixes.destination.ipv4.Ipv4Prefixes;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev180329.ipv4.prefixes.destination.ipv4.Ipv4PrefixesBuilder;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev180329.update.attributes.mp.reach.nlri.advertized.routes.destination.type.DestinationIpv4CaseBuilder;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.Update;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.open.message.BgpParameters;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.path.attributes.Attributes;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.path.attributes.AttributesBuilder;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.update.message.Nlri;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.BgpAddPathTableType;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.BgpTableType;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.RouteRefresh;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.SendReceive;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.mp.capabilities.GracefulRestartCapability;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.mp.capabilities.add.path.capability.AddressFamilies;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpReachNlri;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpReachNlriBuilder;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpUnreachNlri;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpUnreachNlriBuilder;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.mp.reach.nlri.AdvertizedRoutesBuilder;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.mp.unreach.nlri.WithdrawnRoutesBuilder;
87 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.peer.rpc.rev180329.BgpPeerRpcService;
88 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.peer.rpc.rev180329.PeerContext;
89 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.PeerRole;
90 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.PeerKey;
91 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.peer.AdjRibOut;
92 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.Tables;
93 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.TablesKey;
94 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.AddressFamily;
95 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.ClusterIdentifier;
96 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.Ipv4AddressFamily;
97 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.RouteTarget;
98 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.SubsequentAddressFamily;
99 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.UnicastSubsequentAddressFamily;
100 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
101 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
102 import org.opendaylight.yangtools.yang.binding.Notification;
103 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
104 import org.slf4j.Logger;
105 import org.slf4j.LoggerFactory;
106
107 /**
108  * Class representing a peer. We have a single instance for each peer, which provides translation from BGP events into
109  * RIB actions.
110  */
111 public class BGPPeer extends AbstractPeer implements BGPSessionListener {
112     private static final Logger LOG = LoggerFactory.getLogger(BGPPeer.class);
113     private static final TablesKey IPV4_UCAST_TABLE_KEY = new TablesKey(Ipv4AddressFamily.class,
114         UnicastSubsequentAddressFamily.class);
115
116     private Set<TablesKey> tables = Collections.emptySet();
117     private final RIB rib;
118     private final Map<TablesKey, AdjRibOutListener> adjRibOutListenerSet = new HashMap<>();
119     private final List<RouteTarget> rtMemberships = new ArrayList<>();
120     private final RpcProviderRegistry rpcRegistry;
121     private final BGPTableTypeRegistryConsumer tableTypeRegistry;
122     private final BgpPeer bgpPeer;
123     private InstanceIdentifier<AdjRibOut> peerRibOutIId;
124     private KeyedInstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib
125             .rev180329.bgp.rib.rib.Peer, PeerKey> peerIId;
126     @GuardedBy("this")
127     private AbstractRegistration trackerRegistration;
128     private final LoadingCache<TablesKey, KeyedInstanceIdentifier<Tables, TablesKey>> tablesIId
129             = CacheBuilder.newBuilder()
130             .build(new CacheLoader<TablesKey, KeyedInstanceIdentifier<Tables, TablesKey>>() {
131                 @Override
132                 public KeyedInstanceIdentifier<Tables, TablesKey> load(final TablesKey tablesKey) {
133                     return BGPPeer.this.peerRibOutIId.child(Tables.class, tablesKey);
134                 }
135             });
136
137     @GuardedBy("this")
138     private BGPSession session;
139     @GuardedBy("this")
140     private AdjRibInWriter ribWriter;
141     @GuardedBy("this")
142     private EffectiveRibInWriter effRibInWriter;
143     private RoutedRpcRegistration<BgpPeerRpcService> rpcRegistration;
144     private Map<TablesKey, SendReceive> addPathTableMaps = Collections.emptyMap();
145     private YangInstanceIdentifier peerPath;
146     private boolean sessionUp;
147     private boolean llgrSupport;
148     private Stopwatch peerRestartStopwatch;
149     private long selectionDeferralTimerSeconds;
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     @GuardedBy("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 : this.tables) {
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.session = session;
355         this.sessionUp = true;
356         this.bindingChain = this.rib.createPeerChain(this);
357         if (this.session instanceof BGPSessionStateProvider) {
358             ((BGPSessionStateProvider) this.session).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         this.addPathTableMaps = mapTableTypesFamilies(addPathTablesType);
377         final boolean restartingLocally = isLocalRestarting();
378
379         if (!isRestartingGracefully()) {
380             this.rawIdentifier = InetAddresses.forString(session.getBgpId().getValue()).getAddress();
381             this.peerId = RouterIds.createPeerId(session.getBgpId());
382             this.peerIId = getInstanceIdentifier().child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns
383                             .yang.bgp.rib.rev180329.bgp.rib.rib.Peer.class, new PeerKey(this.peerId));
384             this.peerPath = createPeerPath();
385             this.peerRibOutIId = peerIId.child(AdjRibOut.class);
386             this.trackerRegistration = this.rib.getPeerTracker().registerPeer(this);
387             createEffRibInWriter();
388             registerPrefixesCounters(this.effRibInWriter, this.effRibInWriter);
389
390             this.effRibInWriter.init();
391             this.ribWriter = this.ribWriter.transform(this.peerId, this.peerPath, this.rib.getRibSupportContext(),
392                     this.tables, this.addPathTableMaps);
393
394             if (this.rpcRegistry != null) {
395                 this.rpcRegistration = this.rpcRegistry.addRoutedRpcImplementation(BgpPeerRpcService.class,
396                         new BgpPeerRpc(this, session, this.tables));
397                 final KeyedInstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib
398                         .rev180329.bgp.rib.rib.Peer, PeerKey> path = this.rib.getInstanceIdentifier()
399                         .child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp
400                             .rib.rib.Peer.class, new PeerKey(this.peerId));
401                 this.rpcRegistration.registerPath(PeerContext.class, path);
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(), false, 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.getLongLiveStaleTime().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 : this.tables) {
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.session.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.createPeerChain(this),
472                 this.peerIId, 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     @GuardedBy("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     @GuardedBy("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.session instanceof BGPSessionImpl) {
494             final ChannelOutputLimiter limiter = ((BGPSessionImpl) this.session).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 e) {
505         if (e.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, e);
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         if (this.session != null) {
545             try {
546                 if (isRestartingGracefully()) {
547                     this.session.closeWithoutMessage();
548                 } else {
549                     this.session.close();
550                 }
551             } catch (final Exception e) {
552                 LOG.warn("Error closing session with peer", e);
553             }
554             this.session = null;
555         }
556         return future;
557     }
558
559     private Set<TablesKey> getGracefulTables() {
560         return this.tables.stream()
561                 .filter(this::isGracefulRestartReceived)
562                 .filter(this::isGracefulRestartAdvertized)
563                 .collect(Collectors.toSet());
564     }
565
566     private synchronized FluentFuture<? extends CommitInfo> terminateConnection() {
567         final FluentFuture<? extends CommitInfo> future;
568         if (this.trackerRegistration != null) {
569             this.trackerRegistration.close();
570             this.trackerRegistration = null;
571         }
572         if (this.rpcRegistration != null) {
573             this.rpcRegistration.close();
574         }
575         this.ribWriter.releaseChain();
576
577         if (this.effRibInWriter != null) {
578             this.effRibInWriter.close();
579         }
580         this.tables = Collections.emptySet();
581         this.addPathTableMaps = Collections.emptyMap();
582         future = removePeer(this.peerPath);
583         resetState();
584
585         return future;
586     }
587
588     /**
589      * If Graceful Restart Timer expires, remove all routes advertised by peer.
590      */
591     private synchronized void handleRestartTimer() {
592         if (!isPeerRestarting()) {
593             return;
594         }
595
596         final long peerRestartTimeNanos = TimeUnit.SECONDS.toNanos(getPeerRestartTime());
597         final long elapsedNanos = this.peerRestartStopwatch.elapsed(TimeUnit.NANOSECONDS);
598         if (elapsedNanos >= peerRestartTimeNanos) {
599             setAfiSafiGracefulRestartState(0, false, false);
600             onSessionTerminated(this.session, new BGPTerminationReason(BGPError.HOLD_TIMER_EXPIRED));
601         }
602         new ScheduledThreadPoolExecutor(1)
603                 .schedule(this::handleRestartTimer, peerRestartTimeNanos - elapsedNanos, TimeUnit.NANOSECONDS);
604     }
605
606     private synchronized void handleSelectionReferralTimer() {
607         if (!isLocalRestarting()) {
608             return;
609         }
610
611         final long referalTimerNanos = TimeUnit.SECONDS.toNanos(this.selectionDeferralTimerSeconds);
612         final long elapsedNanos = this.peerRestartStopwatch.elapsed(TimeUnit.NANOSECONDS);
613         if (elapsedNanos >= referalTimerNanos) {
614             this.missingEOT.clear();
615             handleGracefulEndOfRib();
616         }
617         new ScheduledThreadPoolExecutor(1)
618                 .schedule(this::handleSelectionReferralTimer, referalTimerNanos - elapsedNanos, TimeUnit.NANOSECONDS);
619     }
620
621     private void releaseConnectionGracefully() {
622         if (getPeerRestartTime() > 0) {
623             setRestartingState();
624         }
625         releaseConnection();
626     }
627
628     @SuppressFBWarnings("IS2_INCONSISTENT_SYNC")
629     @Override
630     public SendReceive getSupportedAddPathTables(final TablesKey tableKey) {
631         return this.addPathTableMaps.get(tableKey);
632     }
633
634     @Override
635     public boolean supportsTable(final TablesKey tableKey) {
636         return this.tables.contains(tableKey) && this.sessionUp;
637     }
638
639     @Override
640     public KeyedInstanceIdentifier<Tables, TablesKey> getRibOutIId(final TablesKey tablesKey) {
641         return this.tablesIId.getUnchecked(tablesKey);
642     }
643
644     @Override
645     public synchronized void onTransactionChainFailed(final TransactionChain<?, ?> chain,
646             final AsyncTransaction<?, ?> transaction, final Throwable cause) {
647         LOG.error("Transaction domChain failed.", cause);
648         releaseConnection();
649     }
650
651     @Override
652     public synchronized void markUptodate(final TablesKey tablesKey) {
653         this.ribWriter.markTableUptodate(tablesKey);
654     }
655
656     @Override
657     public synchronized BGPSessionState getBGPSessionState() {
658         if (this.session instanceof BGPSessionStateProvider) {
659             return ((BGPSessionStateProvider) this.session).getBGPSessionState();
660         }
661         return null;
662     }
663
664     @Override
665     public synchronized BGPTimersState getBGPTimersState() {
666         if (this.session instanceof BGPSessionStateProvider) {
667             return ((BGPSessionStateProvider) this.session).getBGPTimersState();
668         }
669         return null;
670     }
671
672     @Override
673     public synchronized BGPTransportState getBGPTransportState() {
674         if (this.session instanceof BGPSessionStateProvider) {
675             return ((BGPSessionStateProvider) this.session).getBGPTransportState();
676         }
677         return null;
678     }
679
680     @Override
681     public List<RouteTarget> getMemberships() {
682         return this.rtMemberships;
683     }
684
685     @Override
686     public synchronized ListenableFuture<?> restartGracefully(final long selectionDeferralTimerSeconds) {
687         final Set<TablesKey> tablesToPreserve = getGracefulTables();
688         if (tablesToPreserve == null || tablesToPreserve.isEmpty()) {
689             LOG.info("Peer {} is not capable of graceful restart or have no matching graceful tables.", this.peerId);
690             return Futures.immediateFailedFuture(new UnsupportedOperationException(
691                     "Peer is not capable of graceful restart"));
692         }
693         setGracefulPreferences(true, tablesToPreserve);
694         this.selectionDeferralTimerSeconds = selectionDeferralTimerSeconds;
695         setLocalRestartingState(true);
696         return releaseConnection();
697     }
698
699     @Override
700     boolean supportsLLGR() {
701         return this.llgrSupport;
702     }
703
704     private synchronized void setGracefulPreferences(final boolean localRestarting,
705                                                      final Set<TablesKey> preservedTables) {
706         final Set<TablesKey> gracefulTables = this.tables.stream()
707                 .filter(this::isGracefulRestartAdvertized)
708                 .collect(Collectors.toSet());
709         final BgpParameters bgpParameters = GracefulRestartUtil.getGracefulBgpParameters(
710                 this.bgpPeer.getBgpFixedCapabilities(), gracefulTables, preservedTables,
711                 this.bgpPeer.getGracefulRestartTimer(), localRestarting, Collections.emptySet());
712         final BGPSessionPreferences oldPrefs = this.rib.getDispatcher().getBGPPeerRegistry()
713                 .getPeerPreferences(getNeighborAddress());
714         final BGPSessionPreferences newPrefs = new BGPSessionPreferences(
715                 oldPrefs.getMyAs(),
716                 oldPrefs.getHoldTime(),
717                 oldPrefs.getBgpId(),
718                 oldPrefs.getExpectedRemoteAs(),
719                 Collections.singletonList(bgpParameters),
720                 oldPrefs.getMd5Password());
721         this.rib.getDispatcher().getBGPPeerRegistry()
722                 .updatePeerPreferences(getNeighborAddress(), newPrefs);
723     }
724 }