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