Migrate to MD-SAL APIs
[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(), false, restartingLocally);
428
429         final Map<TablesKey, Integer> llTablesReceived;
430         if (advertisedLLTables != null) {
431             llTablesReceived = new HashMap<>();
432             for (org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.mp
433                     .capabilities.ll.graceful.restart.capability.Tables table : advertisedLLTables) {
434                 llTablesReceived.put(new TablesKey(table.getAfi(), table.getSafi()),
435                     table.getLongLivedStaleTime().intValue());
436             }
437         } else {
438             llTablesReceived = Collections.emptyMap();
439         }
440         setAdvertizedLlGracefulRestartTableTypes(llTablesReceived);
441
442         if (!llTablesReceived.isEmpty()) {
443             llgrSupport = true;
444             // FIXME: propagate preserved tables
445         } else {
446             // FIXME: clear preserved tables
447             llgrSupport = false;
448         }
449
450         if (!restartingLocally) {
451             addBgp4Support();
452             for (final TablesKey key : getAfiSafisAdvertized()) {
453                 createAdjRibOutListener(key, true);
454             }
455         }
456
457         // SpotBugs does not grok Optional.ifPresent() and thinks we are using unsynchronized access
458         final Optional<RevisedErrorHandlingSupport> errorHandling = this.bgpPeer.getErrorHandling();
459         if (errorHandling.isPresent()) {
460             this.currentSession.addDecoderConstraint(RevisedErrorHandlingSupport.class, errorHandling.get());
461         }
462     }
463
464     private boolean isRestartingGracefully() {
465         return isLocalRestarting() || isPeerRestarting();
466     }
467
468     private synchronized void createEffRibInWriter() {
469         this.effRibInWriter = new EffectiveRibInWriter(this, this.rib,
470             this.rib.createPeerDOMChain(this),
471             this.peerPath, this.tables, this.tableTypeRegistry,
472             this.rtMemberships,
473             this.rtCache);
474     }
475
476     //try to add a support for old-school BGP-4, if peer did not advertise IPv4-Unicast MP capability
477     @Holding("this")
478     private void addBgp4Support() {
479         if (!this.tables.contains(IPV4_UCAST_TABLE_KEY)) {
480             final HashSet<TablesKey> newSet = new HashSet<>(this.tables);
481             newSet.add(IPV4_UCAST_TABLE_KEY);
482             this.tables = ImmutableSet.copyOf(newSet);
483             createAdjRibOutListener(IPV4_UCAST_TABLE_KEY, false);
484         }
485     }
486
487     @Holding("this")
488     private void createAdjRibOutListener(final TablesKey key, final boolean mpSupport) {
489         final RIBSupport<?, ?, ?, ?> ribSupport = this.rib.getRibSupportContext().getRIBSupport(key);
490
491         // not particularly nice
492         if (ribSupport != null && this.currentSession instanceof BGPSessionImpl) {
493             final ChannelOutputLimiter limiter = ((BGPSessionImpl) this.currentSession).getLimiter();
494             final AdjRibOutListener adjRibOut = AdjRibOutListener.create(this.peerId, key,
495                     this.rib.getYangRibId(), this.rib.getCodecsRegistry(), ribSupport,
496                     this.rib.getService(), limiter, mpSupport);
497             this.adjRibOutListenerSet.put(key, adjRibOut);
498             registerPrefixesSentCounter(key, adjRibOut);
499         }
500     }
501
502     @Override
503     public synchronized void onSessionDown(final BGPSession session, final Exception exc) {
504         if (exc.getMessage().equals(BGPSessionImpl.END_OF_INPUT)) {
505             LOG.info("Session with peer {} went down", this.name);
506         } else {
507             LOG.info("Session with peer {} went down", this.name, exc);
508         }
509         releaseConnectionGracefully();
510     }
511
512     @Override
513     public synchronized void onSessionTerminated(final BGPSession session, final BGPTerminationReason cause) {
514         LOG.info("Session with peer {} terminated: {}", this.name, cause);
515         releaseConnectionGracefully();
516     }
517
518     @Override
519     public String toString() {
520         return MoreObjects.toStringHelper(this).add("name", this.name).add("tables", this.tables).toString();
521     }
522
523     @Override
524     public synchronized FluentFuture<? extends CommitInfo> releaseConnection() {
525         LOG.info("Closing session with peer");
526         this.sessionUp = false;
527         this.adjRibOutListenerSet.values().forEach(AdjRibOutListener::close);
528         this.adjRibOutListenerSet.clear();
529         final FluentFuture<? extends CommitInfo> future;
530         if (!isRestartingGracefully()) {
531             future = terminateConnection();
532         } else {
533             final Set<TablesKey> gracefulTables = getGracefulTables();
534             this.ribWriter.storeStaleRoutes(gracefulTables);
535             future = this.ribWriter.clearTables(Sets.difference(this.tables, gracefulTables));
536             if (isPeerRestarting()) {
537                 this.peerRestartStopwatch = Stopwatch.createStarted();
538                 handleRestartTimer();
539             }
540         }
541         releaseBindingChain();
542
543         closeSession();
544         return future;
545     }
546
547     @Holding("this")
548     @SuppressWarnings("checkstyle:illegalCatch")
549     private void closeSession() {
550         if (this.currentSession != null) {
551             try {
552                 if (isRestartingGracefully()) {
553                     this.currentSession.closeWithoutMessage();
554                 } else {
555                     this.currentSession.close();
556                 }
557             } catch (final Exception e) {
558                 LOG.warn("Error closing session with peer", e);
559             }
560             this.currentSession = null;
561         }
562     }
563
564     private Set<TablesKey> getGracefulTables() {
565         return this.tables.stream()
566                 .filter(this::isGracefulRestartReceived)
567                 .filter(this::isGracefulRestartAdvertized)
568                 .collect(Collectors.toSet());
569     }
570
571     private synchronized FluentFuture<? extends CommitInfo> terminateConnection() {
572         final FluentFuture<? extends CommitInfo> future;
573         if (this.trackerRegistration != null) {
574             this.trackerRegistration.close();
575             this.trackerRegistration = null;
576         }
577         if (this.rpcRegistration != null) {
578             this.rpcRegistration.close();
579         }
580         this.ribWriter.releaseChain();
581
582         if (this.effRibInWriter != null) {
583             this.effRibInWriter.close();
584         }
585         this.tables = ImmutableSet.of();
586         this.addPathTableMaps = Collections.emptyMap();
587         future = removePeer(this.peerPath);
588         resetState();
589
590         return future;
591     }
592
593     /**
594      * If Graceful Restart Timer expires, remove all routes advertised by peer.
595      */
596     private synchronized void handleRestartTimer() {
597         if (!isPeerRestarting()) {
598             return;
599         }
600
601         final long peerRestartTimeNanos = TimeUnit.SECONDS.toNanos(getPeerRestartTime());
602         final long elapsedNanos = this.peerRestartStopwatch.elapsed(TimeUnit.NANOSECONDS);
603         if (elapsedNanos >= peerRestartTimeNanos) {
604             setAfiSafiGracefulRestartState(0, false, false);
605             onSessionTerminated(this.currentSession, new BGPTerminationReason(BGPError.HOLD_TIMER_EXPIRED));
606         }
607
608         currentSession.schedule(this::handleRestartTimer, peerRestartTimeNanos - elapsedNanos, TimeUnit.NANOSECONDS);
609     }
610
611     private synchronized void handleSelectionReferralTimer() {
612         if (!isLocalRestarting()) {
613             return;
614         }
615
616         final long referalTimerNanos = TimeUnit.SECONDS.toNanos(this.currentSelectionDeferralTimerSeconds);
617         final long elapsedNanos = this.peerRestartStopwatch.elapsed(TimeUnit.NANOSECONDS);
618         if (elapsedNanos >= referalTimerNanos) {
619             this.missingEOT.clear();
620             handleGracefulEndOfRib();
621         }
622         currentSession.schedule(this::handleSelectionReferralTimer, referalTimerNanos - elapsedNanos,
623             TimeUnit.NANOSECONDS);
624     }
625
626     private void releaseConnectionGracefully() {
627         if (getPeerRestartTime() > 0) {
628             setRestartingState();
629         }
630         releaseConnection();
631     }
632
633     @SuppressFBWarnings("IS2_INCONSISTENT_SYNC")
634     @Override
635     public SendReceive getSupportedAddPathTables(final TablesKey tableKey) {
636         return this.addPathTableMaps.get(tableKey);
637     }
638
639     @Override
640     public boolean supportsTable(final TablesKey tableKey) {
641         return this.sessionUp && getAfiSafisAdvertized().contains(tableKey);
642     }
643
644     @Override
645     public KeyedInstanceIdentifier<Tables, TablesKey> getRibOutIId(final TablesKey tablesKey) {
646         return this.tablesIId.getUnchecked(tablesKey);
647     }
648
649     @Override
650     public synchronized void onTransactionChainFailed(final DOMTransactionChain chain,
651             final DOMDataTreeTransaction transaction, final Throwable cause) {
652         LOG.error("Transaction domChain failed.", cause);
653         releaseConnection();
654     }
655
656     @Override
657     public synchronized void onTransactionChainFailed(final TransactionChain chain, final Transaction transaction,
658             final Throwable cause) {
659         LOG.error("Transaction domChain failed.", cause);
660         releaseConnection();
661     }
662
663     @Override
664     public synchronized void markUptodate(final TablesKey tablesKey) {
665         this.ribWriter.markTableUptodate(tablesKey);
666     }
667
668     @Override
669     public synchronized BGPSessionState getBGPSessionState() {
670         if (this.currentSession instanceof BGPSessionStateProvider) {
671             return ((BGPSessionStateProvider) this.currentSession).getBGPSessionState();
672         }
673         return null;
674     }
675
676     @Override
677     public synchronized BGPTimersState getBGPTimersState() {
678         if (this.currentSession instanceof BGPSessionStateProvider) {
679             return ((BGPSessionStateProvider) this.currentSession).getBGPTimersState();
680         }
681         return null;
682     }
683
684     @Override
685     public synchronized BGPTransportState getBGPTransportState() {
686         if (this.currentSession instanceof BGPSessionStateProvider) {
687             return ((BGPSessionStateProvider) this.currentSession).getBGPTransportState();
688         }
689         return null;
690     }
691
692     @Override
693     public List<RouteTarget> getMemberships() {
694         return this.rtMemberships;
695     }
696
697     @Override
698     public synchronized ListenableFuture<?> restartGracefully(final long selectionDeferralTimerSeconds) {
699         final Set<TablesKey> tablesToPreserve = getGracefulTables();
700         if (tablesToPreserve == null || tablesToPreserve.isEmpty()) {
701             LOG.info("Peer {} is not capable of graceful restart or have no matching graceful tables.", this.peerId);
702             return Futures.immediateFailedFuture(new UnsupportedOperationException(
703                     "Peer is not capable of graceful restart"));
704         }
705         setGracefulPreferences(true, tablesToPreserve);
706         this.currentSelectionDeferralTimerSeconds = selectionDeferralTimerSeconds;
707         setLocalRestartingState(true);
708         return releaseConnection();
709     }
710
711     @Override
712     boolean supportsLLGR() {
713         return this.llgrSupport;
714     }
715
716     private synchronized void setGracefulPreferences(final boolean localRestarting,
717                                                      final Set<TablesKey> preservedTables) {
718         final Set<TablesKey> gracefulTables = this.tables.stream()
719                 .filter(this::isGracefulRestartAdvertized)
720                 .collect(Collectors.toSet());
721         final BgpParameters bgpParameters = GracefulRestartUtil.getGracefulBgpParameters(
722                 this.bgpPeer.getBgpFixedCapabilities(), gracefulTables, preservedTables,
723                 this.bgpPeer.getGracefulRestartTimer(), localRestarting, Collections.emptySet());
724         final BGPSessionPreferences oldPrefs = this.rib.getDispatcher().getBGPPeerRegistry()
725                 .getPeerPreferences(getNeighborAddress());
726         final BGPSessionPreferences newPrefs = new BGPSessionPreferences(
727                 oldPrefs.getMyAs(),
728                 oldPrefs.getHoldTime(),
729                 oldPrefs.getBgpId(),
730                 oldPrefs.getExpectedRemoteAs(),
731                 Collections.singletonList(bgpParameters),
732                 oldPrefs.getMd5Password());
733         this.rib.getDispatcher().getBGPPeerRegistry()
734                 .updatePeerPreferences(getNeighborAddress(), newPrefs);
735     }
736 }