Fix some rib-impl warnigs
[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, UnicastSubsequentAddressFamily.class);
114
115     private Set<TablesKey> tables = Collections.emptySet();
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 AbstractRegistration 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 session;
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 Stopwatch peerRestartStopwatch;
147     private long selectionDeferralTimerSeconds;
148     private final List<TablesKey> missingEOT = new ArrayList<>();
149
150     public BGPPeer(
151             final BGPTableTypeRegistryConsumer tableTypeRegistry,
152             final IpAddress neighborAddress,
153             final String peerGroupName,
154             final RIB rib,
155             final PeerRole role,
156             final ClusterIdentifier clusterId,
157             final AsNumber localAs,
158             final RpcProviderRegistry rpcRegistry,
159             final Set<TablesKey> afiSafisAdvertized,
160             final Set<TablesKey> afiSafisGracefulAdvertized,
161             final BgpPeer bgpPeer) {
162         super(rib, Ipv4Util.toStringIP(neighborAddress), peerGroupName, role, clusterId,
163                 localAs, neighborAddress, afiSafisAdvertized, afiSafisGracefulAdvertized, Collections.emptyMap());
164         this.tableTypeRegistry = requireNonNull(tableTypeRegistry);
165         this.rib = requireNonNull(rib);
166         this.rpcRegistry = rpcRegistry;
167         this.bgpPeer = bgpPeer;
168     }
169
170     private static Attributes nextHopToAttribute(final Attributes attrs, final MpReachNlri mpReach) {
171         if (attrs.getCNextHop() == null && mpReach.getCNextHop() != null) {
172             final AttributesBuilder attributesBuilder = new AttributesBuilder(attrs);
173             attributesBuilder.setCNextHop(mpReach.getCNextHop());
174             return attributesBuilder.build();
175         }
176         return attrs;
177     }
178
179     /**
180      * Creates MPReach for the prefixes to be handled in the same way as linkstate routes.
181      *
182      * @param message Update message containing prefixes in NLRI
183      * @return MpReachNlri with prefixes from the nlri field
184      */
185     private static MpReachNlri prefixesToMpReach(final Update message) {
186         final List<Ipv4Prefixes> prefixes = message.getNlri().stream()
187                 .map(n -> new Ipv4PrefixesBuilder().setPrefix(n.getPrefix()).setPathId(n.getPathId()).build())
188                 .collect(Collectors.toList());
189         final MpReachNlriBuilder b = new MpReachNlriBuilder().setAfi(Ipv4AddressFamily.class).setSafi(
190                 UnicastSubsequentAddressFamily.class).setAdvertizedRoutes(
191                 new AdvertizedRoutesBuilder().setDestinationType(
192                         new DestinationIpv4CaseBuilder().setDestinationIpv4(
193                                 new DestinationIpv4Builder().setIpv4Prefixes(prefixes).build()).build()).build());
194         if (message.getAttributes() != null) {
195             b.setCNextHop(message.getAttributes().getCNextHop());
196         }
197         return b.build();
198     }
199
200     /**
201      * Create MPUnreach for the prefixes to be handled in the same way as linkstate routes.
202      *
203      * @param message            Update message containing withdrawn routes
204      * @param isAnyNlriAnnounced isAnyNlriAnnounced
205      * @return MpUnreachNlri with prefixes from the withdrawn routes field
206      */
207     private static MpUnreachNlri prefixesToMpUnreach(final Update message, final boolean isAnyNlriAnnounced) {
208         final List<Ipv4Prefixes> prefixes = new ArrayList<>();
209         message.getWithdrawnRoutes().forEach(w -> {
210
211             Optional<Nlri> nlriAnounced = Optional.empty();
212             if (isAnyNlriAnnounced) {
213                 nlriAnounced = message.getNlri().stream().filter(n -> Objects.equal(n.getPrefix(), w.getPrefix())
214                         && Objects.equal(n.getPathId(), w.getPathId()))
215                         .findAny();
216             }
217             if (!nlriAnounced.isPresent()) {
218                 prefixes.add(new Ipv4PrefixesBuilder().setPrefix(w.getPrefix()).setPathId(w.getPathId()).build());
219             }
220         });
221         return new MpUnreachNlriBuilder().setAfi(Ipv4AddressFamily.class).setSafi(UnicastSubsequentAddressFamily.class)
222                 .setWithdrawnRoutes(new WithdrawnRoutesBuilder().setDestinationType(new org.opendaylight.yang.gen.v1
223                         .urn.opendaylight.params.xml.ns.yang.bgp.inet.rev180329.update.attributes.mp.unreach.nlri
224                         .withdrawn.routes.destination.type.DestinationIpv4CaseBuilder().setDestinationIpv4(
225                         new DestinationIpv4Builder().setIpv4Prefixes(prefixes).build()).build()).build()).build();
226     }
227
228     private static Map<TablesKey, SendReceive> mapTableTypesFamilies(final List<AddressFamilies> addPathTablesType) {
229         return ImmutableMap.copyOf(addPathTablesType.stream().collect(Collectors.toMap(af -> new TablesKey(af.getAfi(),
230                         af.getSafi()), BgpAddPathTableType::getSendReceive)));
231     }
232
233     public synchronized void instantiateServiceInstance() {
234         this.ribWriter = AdjRibInWriter.create(this.rib.getYangRibId(), this.peerRole, this);
235         setActive(true);
236     }
237
238     @Override
239     public synchronized FluentFuture<? extends CommitInfo> close() {
240         final FluentFuture<? extends CommitInfo> future = releaseConnection();
241         closeDomChain();
242         setActive(false);
243         return future;
244     }
245
246     @Override
247     public void onMessage(final BGPSession session, final Notification msg) throws BGPDocumentedException {
248         if (msg instanceof Update) {
249             onUpdateMessage((Update) msg);
250         } else if (msg instanceof RouteRefresh) {
251             onRouteRefreshMessage((RouteRefresh) msg);
252         } else {
253             LOG.info("Ignoring unhandled message class {}", msg.getClass());
254         }
255     }
256
257     private void onRouteRefreshMessage(final RouteRefresh message) {
258         final Class<? extends AddressFamily> rrAfi = message.getAfi();
259         final Class<? extends SubsequentAddressFamily> rrSafi = message.getSafi();
260
261         final TablesKey key = new TablesKey(rrAfi, rrSafi);
262         final AdjRibOutListener listener = this.adjRibOutListenerSet.get(key);
263         if (listener != null) {
264             listener.close();
265             this.adjRibOutListenerSet.remove(key);
266             createAdjRibOutListener(key, listener.isMpSupported());
267         } else {
268             LOG.info("Ignoring RouteRefresh message. Afi/Safi is not supported: {}, {}.", rrAfi, rrSafi);
269         }
270     }
271
272     /**
273      * Check for presence of well known mandatory attribute LOCAL_PREF in Update message.
274      *
275      * @param message Update message
276      */
277     private void checkMandatoryAttributesPresence(final Update message) throws BGPDocumentedException {
278         if (MessageUtil.isAnyNlriPresent(message)) {
279             final Attributes attrs = message.getAttributes();
280             if (this.peerRole == PeerRole.Ibgp && (attrs == null || attrs.getLocalPref() == null)) {
281                 throw new BGPDocumentedException(BGPError.MANDATORY_ATTR_MISSING_MSG + "LOCAL_PREF",
282                         BGPError.WELL_KNOWN_ATTR_MISSING,
283                         new byte[]{LocalPreferenceAttributeParser.TYPE});
284             }
285         }
286     }
287
288     /**
289      * Process Update message received.
290      * Calls {@link #checkMandatoryAttributesPresence(Update)} to check for presence of mandatory attributes.
291      *
292      * @param message Update message
293      */
294     private synchronized void onUpdateMessage(final Update message) throws BGPDocumentedException {
295         checkMandatoryAttributesPresence(message);
296
297         // update AdjRibs
298         final Attributes attrs = message.getAttributes();
299         MpReachNlri mpReach;
300         final boolean isAnyNlriAnnounced = message.getNlri() != null;
301         if (isAnyNlriAnnounced) {
302             mpReach = prefixesToMpReach(message);
303         } else {
304             mpReach = MessageUtil.getMpReachNlri(attrs);
305         }
306         if (mpReach != null) {
307             this.ribWriter.updateRoutes(mpReach, nextHopToAttribute(attrs, mpReach));
308         }
309         MpUnreachNlri mpUnreach;
310         if (message.getWithdrawnRoutes() != null) {
311             mpUnreach = prefixesToMpUnreach(message, isAnyNlriAnnounced);
312         } else {
313             mpUnreach = MessageUtil.getMpUnreachNlri(attrs);
314         }
315         final boolean endOfRib = BgpPeerUtil.isEndOfRib(message);
316         if (mpUnreach != null) {
317             if (endOfRib) {
318                 final TablesKey tablesKey = new TablesKey(mpUnreach.getAfi(), mpUnreach.getSafi());
319                 this.ribWriter.removeStaleRoutes(tablesKey);
320                 this.missingEOT.remove(tablesKey);
321                 handleGracefulEndOfRib();
322             } else {
323                 this.ribWriter.removeRoutes(mpUnreach);
324             }
325         } else if (endOfRib) {
326             this.ribWriter.removeStaleRoutes(IPV4_UCAST_TABLE_KEY);
327             this.missingEOT.remove(IPV4_UCAST_TABLE_KEY);
328             handleGracefulEndOfRib();
329         }
330     }
331
332     private synchronized void handleGracefulEndOfRib() {
333         if (isLocalRestarting()) {
334             if (this.missingEOT.isEmpty()) {
335                 createEffRibInWriter();
336                 this.effRibInWriter.init();
337                 registerPrefixesCounters(this.effRibInWriter, this.effRibInWriter);
338                 for (final TablesKey key : this.tables) {
339                     createAdjRibOutListener(key, true);
340                 }
341                 setLocalRestartingState(false);
342                 setGracefulPreferences(false, Collections.emptySet());
343             }
344         }
345     }
346
347     @Override
348     public synchronized void onSessionUp(final BGPSession session) {
349         this.session = session;
350         this.sessionUp = true;
351         this.bindingChain = this.rib.createPeerChain(this);
352         if (this.session instanceof BGPSessionStateProvider) {
353             ((BGPSessionStateProvider) this.session).registerMessagesCounter(this);
354         }
355         final GracefulRestartCapability advertisedGracefulRestartCapability =
356                 session.getAdvertisedGracefulRestartCapability();
357         final List<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.mp.capabilities.graceful.restart.capability.Tables> advertisedTables =
358                 advertisedGracefulRestartCapability.getTables();
359         final List<AddressFamilies> addPathTablesType = session.getAdvertisedAddPathTableTypes();
360         final Set<BgpTableType> advertizedTableTypes = session.getAdvertisedTableTypes();
361         LOG.info("Session with peer {} went up with tables {} and Add Path tables {}", this.name,
362                 advertizedTableTypes, addPathTablesType);
363         final Set<TablesKey> setTables = advertizedTableTypes.stream().map(t -> new TablesKey(t.getAfi(), t.getSafi()))
364                 .collect(Collectors.toSet());
365         this.tables = ImmutableSet.copyOf(setTables);
366         this.addPathTableMaps = mapTableTypesFamilies(addPathTablesType);
367         final boolean restartingLocally = isLocalRestarting();
368
369         if (!isRestartingGracefully()) {
370             this.rawIdentifier = InetAddresses.forString(session.getBgpId().getValue()).getAddress();
371             this.peerId = RouterIds.createPeerId(session.getBgpId());
372             this.peerIId = getInstanceIdentifier().child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns
373                             .yang.bgp.rib.rev180329.bgp.rib.rib.Peer.class, new PeerKey(this.peerId));
374             this.peerPath = createPeerPath();
375             this.peerRibOutIId = peerIId.child(AdjRibOut.class);
376             this.trackerRegistration = this.rib.getPeerTracker().registerPeer(this);
377             createEffRibInWriter();
378             registerPrefixesCounters(this.effRibInWriter, this.effRibInWriter);
379
380             this.effRibInWriter.init();
381             this.ribWriter = this.ribWriter.transform(this.peerId, this.peerPath, this.rib.getRibSupportContext(),
382                     this.tables, this.addPathTableMaps);
383
384             if (this.rpcRegistry != null) {
385                 this.rpcRegistration = this.rpcRegistry.addRoutedRpcImplementation(BgpPeerRpcService.class,
386                         new BgpPeerRpc(this, session, this.tables));
387                 final KeyedInstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib
388                         .rev180329.bgp.rib.rib.Peer, PeerKey> path = this.rib.getInstanceIdentifier()
389                         .child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib
390                                 .rib.Peer.class, new PeerKey(this.peerId));
391                 this.rpcRegistration.registerPath(PeerContext.class, path);
392             }
393         } else {
394             final Set<TablesKey> forwardingTables;
395             if (advertisedTables == null) {
396                 forwardingTables = Collections.emptySet();
397             } else {
398                 forwardingTables = advertisedTables.stream()
399                         .filter(table -> table.getAfiFlags() != null)
400                         .filter(table -> table.getAfiFlags().isForwardingState())
401                         .map(table -> new TablesKey(table.getAfi(), table.getSafi()))
402                         .collect(Collectors.toSet());
403             }
404             this.ribWriter.clearTables(Sets.difference(this.tables, forwardingTables));
405             if (restartingLocally) {
406                 this.effRibInWriter.close();
407                 this.peerRestartStopwatch = Stopwatch.createStarted();
408                 handleSelectionReferralTimer();
409                 this.missingEOT.addAll(this.tables);
410             }
411         }
412         if (advertisedTables == null || advertisedTables.isEmpty()) {
413             setAdvertizedGracefulRestartTableTypes(Collections.emptyList());
414         } else {
415             setAdvertizedGracefulRestartTableTypes(advertisedTables.stream()
416                     .map(t -> new TablesKey(t.getAfi(), t.getSafi())).collect(Collectors.toList()));
417         }
418         final int restartTime = advertisedGracefulRestartCapability.getRestartTime();
419         setAfiSafiGracefulRestartState(restartTime, false, restartingLocally);
420         if (!restartingLocally) {
421             addBgp4Support();
422             for (final TablesKey key : this.tables) {
423                 createAdjRibOutListener(key, true);
424             }
425         }
426
427         // SpotBugs does not grok Optional.ifPresent() and thinks we are using unsynchronized access
428         final Optional<RevisedErrorHandlingSupport> errorHandling = this.bgpPeer.getErrorHandling();
429         if (errorHandling.isPresent()) {
430             this.session.addDecoderConstraint(RevisedErrorHandlingSupport.class, errorHandling.get());
431         }
432     }
433
434     private boolean isRestartingGracefully() {
435         return isLocalRestarting() || isPeerRestarting();
436     }
437
438     private synchronized void createEffRibInWriter() {
439         this.effRibInWriter = new EffectiveRibInWriter(this, this.rib,
440                 this.rib.createPeerChain(this),
441                 this.peerIId, this.tables, this.tableTypeRegistry,
442                 this.rtMemberships,
443                 this.rtCache);
444     }
445
446     //try to add a support for old-school BGP-4, if peer did not advertise IPv4-Unicast MP capability
447     private synchronized void addBgp4Support() {
448         if (!this.tables.contains(IPV4_UCAST_TABLE_KEY)) {
449             final HashSet<TablesKey> newSet = new HashSet<>(this.tables);
450             newSet.add(IPV4_UCAST_TABLE_KEY);
451             this.tables = ImmutableSet.copyOf(newSet);
452             createAdjRibOutListener(IPV4_UCAST_TABLE_KEY, false);
453         }
454     }
455
456     private synchronized void createAdjRibOutListener(final TablesKey key,
457                                                       final boolean mpSupport) {
458         final RIBSupport<?, ?, ?, ?> ribSupport = this.rib.getRibSupportContext().getRIBSupport(key);
459
460         // not particularly nice
461         if (ribSupport != null && this.session instanceof BGPSessionImpl) {
462             final ChannelOutputLimiter limiter = ((BGPSessionImpl) this.session).getLimiter();
463             final AdjRibOutListener adjRibOut = AdjRibOutListener.create(this.peerId, key,
464                     this.rib.getYangRibId(), this.rib.getCodecsRegistry(), ribSupport,
465                     this.rib.getService(), limiter, mpSupport);
466             this.adjRibOutListenerSet.put(key, adjRibOut);
467             registerPrefixesSentCounter(key, adjRibOut);
468         }
469     }
470
471     @Override
472     public synchronized void onSessionDown(final BGPSession session, final Exception e) {
473         if (e.getMessage().equals(BGPSessionImpl.END_OF_INPUT)) {
474             LOG.info("Session with peer {} went down", this.name);
475         } else {
476             LOG.info("Session with peer {} went down", this.name, e);
477         }
478         releaseConnectionGracefully();
479     }
480
481     @Override
482     public synchronized void onSessionTerminated(final BGPSession session, final BGPTerminationReason cause) {
483         LOG.info("Session with peer {} terminated: {}", this.name, cause);
484         releaseConnectionGracefully();
485     }
486
487     @Override
488     public String toString() {
489         return MoreObjects.toStringHelper(this).add("name", this.name).add("tables", this.tables).toString();
490     }
491
492     @Override
493     public synchronized FluentFuture<? extends CommitInfo> releaseConnection() {
494         LOG.info("Closing session with peer");
495         this.sessionUp = false;
496         this.adjRibOutListenerSet.values().forEach(AdjRibOutListener::close);
497         this.adjRibOutListenerSet.clear();
498         final FluentFuture<? extends CommitInfo> future;
499         if (!isRestartingGracefully()) {
500             future = terminateConnection();
501         } else {
502             final Set<TablesKey> gracefulTables = getGracefulTables();
503             this.ribWriter.storeStaleRoutes(gracefulTables);
504             future = this.ribWriter.clearTables(Sets.difference(this.tables, gracefulTables));
505             if (isPeerRestarting()) {
506                 this.peerRestartStopwatch = Stopwatch.createStarted();
507                 handleRestartTimer();
508             }
509         }
510         releaseBindingChain();
511
512         if (this.session != null) {
513             try {
514                 if (isRestartingGracefully()) {
515                     this.session.closeWithoutMessage();
516                 } else {
517                     this.session.close();
518                 }
519             } catch (final Exception e) {
520                 LOG.warn("Error closing session with peer", e);
521             }
522             this.session = null;
523         }
524         return future;
525     }
526
527     private Set<TablesKey> getGracefulTables() {
528         return this.tables.stream()
529                 .filter(this::isGracefulRestartReceived)
530                 .filter(this::isGracefulRestartAdvertized)
531                 .collect(Collectors.toSet());
532     }
533
534     private synchronized FluentFuture<? extends CommitInfo> terminateConnection() {
535         final FluentFuture<? extends CommitInfo> future;
536         if (this.trackerRegistration != null) {
537             this.trackerRegistration.close();
538             this.trackerRegistration = null;
539         }
540         if (this.rpcRegistration != null) {
541             this.rpcRegistration.close();
542         }
543         this.ribWriter.releaseChain();
544
545         if (this.effRibInWriter != null) {
546             this.effRibInWriter.close();
547         }
548         this.tables = Collections.emptySet();
549         this.addPathTableMaps = Collections.emptyMap();
550         future = removePeer(this.peerPath);
551         resetState();
552
553         return future;
554     }
555
556     /**
557      * If Graceful Restart Timer expires, remove all routes advertised by peer.
558      */
559     private synchronized void handleRestartTimer() {
560         if (!isPeerRestarting()) {
561             return;
562         }
563
564         final long peerRestartTimeNanos = TimeUnit.SECONDS.toNanos(getPeerRestartTime());
565         final long elapsedNanos = this.peerRestartStopwatch.elapsed(TimeUnit.NANOSECONDS);
566         if (elapsedNanos >= peerRestartTimeNanos) {
567             setAfiSafiGracefulRestartState(0, false, false);
568             onSessionTerminated(this.session, new BGPTerminationReason(BGPError.HOLD_TIMER_EXPIRED));
569         }
570         new ScheduledThreadPoolExecutor(1)
571                 .schedule(this::handleRestartTimer, peerRestartTimeNanos - elapsedNanos, TimeUnit.NANOSECONDS);
572     }
573
574     private synchronized void handleSelectionReferralTimer() {
575         if (!isLocalRestarting()) {
576             return;
577         }
578
579         final long referalTimerNanos = TimeUnit.SECONDS.toNanos(this.selectionDeferralTimerSeconds);
580         final long elapsedNanos = this.peerRestartStopwatch.elapsed(TimeUnit.NANOSECONDS);
581         if (elapsedNanos >= referalTimerNanos) {
582             this.missingEOT.clear();
583             handleGracefulEndOfRib();
584         }
585         new ScheduledThreadPoolExecutor(1)
586                 .schedule(this::handleSelectionReferralTimer, referalTimerNanos - elapsedNanos, TimeUnit.NANOSECONDS);
587     }
588
589     private void releaseConnectionGracefully() {
590         if (getPeerRestartTime() > 0) {
591             setRestartingState();
592         }
593         releaseConnection();
594     }
595
596     @SuppressFBWarnings("IS2_INCONSISTENT_SYNC")
597     @Override
598     public SendReceive getSupportedAddPathTables(final TablesKey tableKey) {
599         return this.addPathTableMaps.get(tableKey);
600     }
601
602     @Override
603     public boolean supportsTable(final TablesKey tableKey) {
604         return this.tables.contains(tableKey) && this.sessionUp;
605     }
606
607     @Override
608     public KeyedInstanceIdentifier<Tables, TablesKey> getRibOutIId(final TablesKey tablesKey) {
609         return this.tablesIId.getUnchecked(tablesKey);
610     }
611
612     @Override
613     public synchronized void onTransactionChainFailed(final TransactionChain<?, ?> chain,
614             final AsyncTransaction<?, ?> transaction, final Throwable cause) {
615         LOG.error("Transaction domChain failed.", cause);
616         releaseConnection();
617     }
618
619     @Override
620     public synchronized void markUptodate(final TablesKey tablesKey) {
621         this.ribWriter.markTableUptodate(tablesKey);
622     }
623
624     @Override
625     public synchronized BGPSessionState getBGPSessionState() {
626         if (this.session instanceof BGPSessionStateProvider) {
627             return ((BGPSessionStateProvider) this.session).getBGPSessionState();
628         }
629         return null;
630     }
631
632     @Override
633     public synchronized BGPTimersState getBGPTimersState() {
634         if (this.session instanceof BGPSessionStateProvider) {
635             return ((BGPSessionStateProvider) this.session).getBGPTimersState();
636         }
637         return null;
638     }
639
640     @Override
641     public synchronized BGPTransportState getBGPTransportState() {
642         if (this.session instanceof BGPSessionStateProvider) {
643             return ((BGPSessionStateProvider) this.session).getBGPTransportState();
644         }
645         return null;
646     }
647
648     @Override
649     public List<RouteTarget> getMemberships() {
650         return this.rtMemberships;
651     }
652
653     @Override
654     public synchronized ListenableFuture<?> restartGracefully(final long selectionDeferralTimerSeconds) {
655         final Set<TablesKey> tablesToPreserve = getGracefulTables();
656         if (tablesToPreserve == null || tablesToPreserve.isEmpty()) {
657             LOG.info("Peer {} is not capable of graceful restart or have no matching graceful tables.", this.peerId);
658             return Futures.immediateFailedFuture(new UnsupportedOperationException(
659                     "Peer is not capable of graceful restart"));
660         }
661         setGracefulPreferences(true, tablesToPreserve);
662         this.selectionDeferralTimerSeconds = selectionDeferralTimerSeconds;
663         setLocalRestartingState(true);
664         return releaseConnection();
665     }
666
667     private synchronized void setGracefulPreferences(final boolean localRestarting,
668                                                      final Set<TablesKey> preservedTables) {
669         final Set<TablesKey> gracefulTables = this.tables.stream()
670                 .filter(this::isGracefulRestartAdvertized)
671                 .collect(Collectors.toSet());
672         final BgpParameters bgpParameters = GracefulRestartUtil.getGracefulBgpParameters(
673                 this.bgpPeer.getBgpFixedCapabilities(), gracefulTables, preservedTables,
674                 this.bgpPeer.getGracefulRestartTimer(), localRestarting, Collections.emptySet());
675         final BGPSessionPreferences oldPrefs = this.rib.getDispatcher().getBGPPeerRegistry()
676                 .getPeerPreferences(getNeighborAddress());
677         final BGPSessionPreferences newPrefs = new BGPSessionPreferences(
678                 oldPrefs.getMyAs(),
679                 oldPrefs.getHoldTime(),
680                 oldPrefs.getBgpId(),
681                 oldPrefs.getExpectedRemoteAs(),
682                 Collections.singletonList(bgpParameters),
683                 oldPrefs.getMd5Password());
684         this.rib.getDispatcher().getBGPPeerRegistry()
685                 .updatePeerPreferences(getNeighborAddress(), newPrefs);
686     }
687 }