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