Route Constrain policies
[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.cache.CacheBuilder;
15 import com.google.common.cache.CacheLoader;
16 import com.google.common.cache.LoadingCache;
17 import com.google.common.collect.ImmutableMap;
18 import com.google.common.collect.ImmutableSet;
19 import com.google.common.net.InetAddresses;
20 import com.google.common.util.concurrent.FluentFuture;
21 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
22 import java.util.ArrayList;
23 import java.util.Collections;
24 import java.util.HashMap;
25 import java.util.HashSet;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Optional;
29 import java.util.Set;
30 import java.util.stream.Collectors;
31 import javax.annotation.concurrent.GuardedBy;
32 import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
33 import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
34 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RoutedRpcRegistration;
35 import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
36 import org.opendaylight.mdsal.common.api.CommitInfo;
37 import org.opendaylight.protocol.bgp.openconfig.spi.BGPTableTypeRegistryConsumer;
38 import org.opendaylight.protocol.bgp.parser.BGPDocumentedException;
39 import org.opendaylight.protocol.bgp.parser.BGPError;
40 import org.opendaylight.protocol.bgp.parser.impl.message.update.LocalPreferenceAttributeParser;
41 import org.opendaylight.protocol.bgp.parser.spi.MessageUtil;
42 import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
43 import org.opendaylight.protocol.bgp.rib.impl.state.BGPSessionStateProvider;
44 import org.opendaylight.protocol.bgp.rib.spi.BGPSession;
45 import org.opendaylight.protocol.bgp.rib.spi.BGPSessionListener;
46 import org.opendaylight.protocol.bgp.rib.spi.BGPTerminationReason;
47 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
48 import org.opendaylight.protocol.bgp.rib.spi.RouterIds;
49 import org.opendaylight.protocol.bgp.rib.spi.state.BGPSessionState;
50 import org.opendaylight.protocol.bgp.rib.spi.state.BGPTimersState;
51 import org.opendaylight.protocol.bgp.rib.spi.state.BGPTransportState;
52 import org.opendaylight.protocol.bgp.route.targetcontrain.spi.ClientRouteTargetContrainCache;
53 import org.opendaylight.protocol.concepts.AbstractRegistration;
54 import org.opendaylight.protocol.util.Ipv4Util;
55 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.AsNumber;
56 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev180329.ipv4.prefixes.DestinationIpv4Builder;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev180329.ipv4.prefixes.destination.ipv4.Ipv4Prefixes;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev180329.ipv4.prefixes.destination.ipv4.Ipv4PrefixesBuilder;
60 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;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.Update;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.path.attributes.Attributes;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.path.attributes.AttributesBuilder;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.update.message.Nlri;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.BgpAddPathTableType;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.BgpTableType;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.RouteRefresh;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.SendReceive;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.mp.capabilities.add.path.capability.AddressFamilies;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpReachNlri;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpReachNlriBuilder;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpUnreachNlri;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpUnreachNlriBuilder;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.mp.reach.nlri.AdvertizedRoutesBuilder;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.mp.unreach.nlri.WithdrawnRoutesBuilder;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.peer.rpc.rev180329.BgpPeerRpcService;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.peer.rpc.rev180329.PeerContext;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.PeerRole;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.PeerKey;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.peer.AdjRibOut;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.Tables;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.TablesKey;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.route.target.constrain.rev180618.route.target.constrain.routes.route.target.constrain.routes.RouteTargetConstrainRoute;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.AddressFamily;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.ClusterIdentifier;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.Ipv4AddressFamily;
87 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.RouteTarget;
88 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.SubsequentAddressFamily;
89 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.UnicastSubsequentAddressFamily;
90 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
91 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
92 import org.opendaylight.yangtools.yang.binding.Notification;
93 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
94 import org.slf4j.Logger;
95 import org.slf4j.LoggerFactory;
96
97 /**
98  * Class representing a peer. We have a single instance for each peer, which provides translation from BGP events into
99  * RIB actions.
100  */
101 public class BGPPeer extends AbstractPeer implements BGPSessionListener {
102     private static final Logger LOG = LoggerFactory.getLogger(BGPPeer.class);
103
104     private Set<TablesKey> tables = Collections.emptySet();
105     private final RIB rib;
106     private final Map<TablesKey, AdjRibOutListener> adjRibOutListenerSet = new HashMap<>();
107     private final List<RouteTarget> rtMemberships = new ArrayList<>();
108     private final RpcProviderRegistry rpcRegistry;
109     private final BGPTableTypeRegistryConsumer tableTypeRegistry;
110     private InstanceIdentifier<AdjRibOut> peerRibOutIId;
111     @GuardedBy("this")
112     private AbstractRegistration trackerRegistration;
113     private final LoadingCache<TablesKey, KeyedInstanceIdentifier<Tables, TablesKey>> tablesIId
114             = CacheBuilder.newBuilder()
115             .build(new CacheLoader<TablesKey, KeyedInstanceIdentifier<Tables, TablesKey>>() {
116                 @Override
117                 public KeyedInstanceIdentifier<Tables, TablesKey> load(final TablesKey tablesKey) {
118                     return BGPPeer.this.peerRibOutIId.child(Tables.class, tablesKey);
119                 }
120             });
121
122     @GuardedBy("this")
123     private BGPSession session;
124     @GuardedBy("this")
125     private AdjRibInWriter ribWriter;
126     @GuardedBy("this")
127     private EffectiveRibInWriter effRibInWriter;
128     private RoutedRpcRegistration<BgpPeerRpcService> rpcRegistration;
129     private Map<TablesKey, SendReceive> addPathTableMaps = Collections.emptyMap();
130     private YangInstanceIdentifier peerPath;
131     private boolean sessionUp;
132
133     public BGPPeer(
134             final BGPTableTypeRegistryConsumer tableTypeRegistry,
135             final IpAddress neighborAddress,
136             final String peerGroupName,
137             final RIB rib,
138             final PeerRole role,
139             final ClusterIdentifier clusterId,
140             final AsNumber localAs,
141             final RpcProviderRegistry rpcRegistry,
142             final Set<TablesKey> afiSafisAdvertized,
143             final Set<TablesKey> afiSafisGracefulAdvertized) {
144         super(rib, Ipv4Util.toStringIP(neighborAddress), peerGroupName, role, clusterId,
145                 localAs, neighborAddress, afiSafisAdvertized, afiSafisGracefulAdvertized);
146         this.tableTypeRegistry = requireNonNull(tableTypeRegistry);
147         this.rib = requireNonNull(rib);
148         this.rpcRegistry = rpcRegistry;
149     }
150
151     BGPPeer(
152             final BGPTableTypeRegistryConsumer tableTypeRegistry,
153             final IpAddress neighborAddress,
154             final RIB rib,
155             final PeerRole role,
156             final RpcProviderRegistry rpcRegistry,
157             final Set<TablesKey> afiSafisAdvertized,
158             final Set<TablesKey> afiSafisGracefulAdvertized) {
159         this(tableTypeRegistry, neighborAddress, null, rib, role, null, null, rpcRegistry,
160                 afiSafisAdvertized, afiSafisGracefulAdvertized);
161     }
162
163
164     private static Attributes nextHopToAttribute(final Attributes attrs, final MpReachNlri mpReach) {
165         if (attrs.getCNextHop() == null && mpReach.getCNextHop() != null) {
166             final AttributesBuilder attributesBuilder = new AttributesBuilder(attrs);
167             attributesBuilder.setCNextHop(mpReach.getCNextHop());
168             return attributesBuilder.build();
169         }
170         return attrs;
171     }
172
173     /**
174      * Creates MPReach for the prefixes to be handled in the same way as linkstate routes.
175      *
176      * @param message Update message containing prefixes in NLRI
177      * @return MpReachNlri with prefixes from the nlri field
178      */
179     private static MpReachNlri prefixesToMpReach(final Update message) {
180         final List<Ipv4Prefixes> prefixes = message.getNlri().stream()
181                 .map(n -> new Ipv4PrefixesBuilder().setPrefix(n.getPrefix()).setPathId(n.getPathId()).build())
182                 .collect(Collectors.toList());
183         final MpReachNlriBuilder b = new MpReachNlriBuilder().setAfi(Ipv4AddressFamily.class).setSafi(
184                 UnicastSubsequentAddressFamily.class).setAdvertizedRoutes(
185                 new AdvertizedRoutesBuilder().setDestinationType(
186                         new DestinationIpv4CaseBuilder().setDestinationIpv4(
187                                 new DestinationIpv4Builder().setIpv4Prefixes(prefixes).build()).build()).build());
188         if (message.getAttributes() != null) {
189             b.setCNextHop(message.getAttributes().getCNextHop());
190         }
191         return b.build();
192     }
193
194     /**
195      * Create MPUnreach for the prefixes to be handled in the same way as linkstate routes.
196      *
197      * @param message            Update message containing withdrawn routes
198      * @param isAnyNlriAnnounced isAnyNlriAnnounced
199      * @return MpUnreachNlri with prefixes from the withdrawn routes field
200      */
201     private static MpUnreachNlri prefixesToMpUnreach(final Update message, final boolean isAnyNlriAnnounced) {
202         final List<Ipv4Prefixes> prefixes = new ArrayList<>();
203         message.getWithdrawnRoutes().forEach(w -> {
204
205             Optional<Nlri> nlriAnounced = Optional.empty();
206             if (isAnyNlriAnnounced) {
207                 nlriAnounced = message.getNlri().stream().filter(n -> Objects.equal(n.getPrefix(), w.getPrefix())
208                         && Objects.equal(n.getPathId(), w.getPathId()))
209                         .findAny();
210             }
211             if (!nlriAnounced.isPresent()) {
212                 prefixes.add(new Ipv4PrefixesBuilder().setPrefix(w.getPrefix()).setPathId(w.getPathId()).build());
213             }
214         });
215         return new MpUnreachNlriBuilder().setAfi(Ipv4AddressFamily.class).setSafi(UnicastSubsequentAddressFamily.class)
216                 .setWithdrawnRoutes(new WithdrawnRoutesBuilder().setDestinationType(new org.opendaylight.yang.gen.v1
217                         .urn.opendaylight.params.xml.ns.yang.bgp.inet.rev180329.update.attributes.mp.unreach.nlri
218                         .withdrawn.routes.destination.type.DestinationIpv4CaseBuilder().setDestinationIpv4(
219                         new DestinationIpv4Builder().setIpv4Prefixes(prefixes).build()).build()).build()).build();
220     }
221
222     private static Map<TablesKey, SendReceive> mapTableTypesFamilies(final List<AddressFamilies> addPathTablesType) {
223         return ImmutableMap.copyOf(addPathTablesType.stream().collect(Collectors.toMap(af -> new TablesKey(af.getAfi(),
224                         af.getSafi()), BgpAddPathTableType::getSendReceive)));
225     }
226
227     public synchronized void instantiateServiceInstance() {
228         this.ribWriter = AdjRibInWriter.create(this.rib.getYangRibId(), this.peerRole, this);
229         setActive(true);
230     }
231
232     @Override
233     public synchronized FluentFuture<? extends CommitInfo> close() {
234         final FluentFuture<? extends CommitInfo> future = releaseConnection();
235         closeDomChain();
236         setActive(false);
237         return future;
238     }
239
240     @Override
241     public void onMessage(final BGPSession session, final Notification msg) throws BGPDocumentedException {
242         if (!(msg instanceof Update) && !(msg instanceof RouteRefresh)) {
243             LOG.info("Ignoring unhandled message class {}", msg.getClass());
244             return;
245         }
246         if (msg instanceof Update) {
247             onUpdateMessage((Update) msg);
248         } else {
249             onRouteRefreshMessage((RouteRefresh) msg);
250         }
251     }
252
253     private void onRouteRefreshMessage(final RouteRefresh message) {
254         final Class<? extends AddressFamily> rrAfi = message.getAfi();
255         final Class<? extends SubsequentAddressFamily> rrSafi = message.getSafi();
256
257         final TablesKey key = new TablesKey(rrAfi, rrSafi);
258         final AdjRibOutListener listener = this.adjRibOutListenerSet.get(key);
259         if (listener != null) {
260             listener.close();
261             this.adjRibOutListenerSet.remove(key);
262             createAdjRibOutListener(key, listener.isMpSupported());
263         } else {
264             LOG.info("Ignoring RouteRefresh message. Afi/Safi is not supported: {}, {}.", rrAfi, rrSafi);
265         }
266     }
267
268     /**
269      * Check for presence of well known mandatory attribute LOCAL_PREF in Update message.
270      *
271      * @param message Update message
272      */
273     private void checkMandatoryAttributesPresence(final Update message) throws BGPDocumentedException {
274         if (MessageUtil.isAnyNlriPresent(message)) {
275             final Attributes attrs = message.getAttributes();
276             if (this.peerRole == PeerRole.Ibgp && (attrs == null || attrs.getLocalPref() == null)) {
277                 throw new BGPDocumentedException(BGPError.MANDATORY_ATTR_MISSING_MSG + "LOCAL_PREF",
278                         BGPError.WELL_KNOWN_ATTR_MISSING,
279                         new byte[]{LocalPreferenceAttributeParser.TYPE});
280             }
281         }
282     }
283
284     /**
285      * Process Update message received.
286      * Calls {@link #checkMandatoryAttributesPresence(Update)} to check for presence of mandatory attributes.
287      *
288      * @param message Update message
289      */
290     private synchronized void onUpdateMessage(final Update message) throws BGPDocumentedException {
291         checkMandatoryAttributesPresence(message);
292
293         // update AdjRibs
294         final Attributes attrs = message.getAttributes();
295         MpReachNlri mpReach;
296         final boolean isAnyNlriAnnounced = message.getNlri() != null;
297         if (isAnyNlriAnnounced) {
298             mpReach = prefixesToMpReach(message);
299         } else {
300             mpReach = MessageUtil.getMpReachNlri(attrs);
301         }
302         if (mpReach != null) {
303             this.ribWriter.updateRoutes(mpReach, nextHopToAttribute(attrs, mpReach));
304         }
305         MpUnreachNlri mpUnreach;
306         if (message.getWithdrawnRoutes() != null) {
307             mpUnreach = prefixesToMpUnreach(message, isAnyNlriAnnounced);
308         } else {
309             mpUnreach = MessageUtil.getMpUnreachNlri(attrs);
310         }
311         if (mpUnreach != null) {
312             this.ribWriter.removeRoutes(mpUnreach);
313         }
314     }
315
316     @Override
317     public synchronized void onSessionUp(final BGPSession session) {
318         this.session = session;
319         this.sessionUp = true;
320         this.bindingChain = this.rib.createPeerChain(this);
321         if (this.session instanceof BGPSessionStateProvider) {
322             ((BGPSessionStateProvider) this.session).registerMessagesCounter(this);
323         }
324
325         final List<AddressFamilies> addPathTablesType = session.getAdvertisedAddPathTableTypes();
326         final Set<BgpTableType> advertizedTableTypes = session.getAdvertisedTableTypes();
327         final List<BgpTableType> advertizedGracefulRestartTableTypes = session.getAdvertisedGracefulRestartTableTypes();
328         LOG.info("Session with peer {} went up with tables {} and Add Path tables {}", this.name,
329                 advertizedTableTypes, addPathTablesType);
330         this.rawIdentifier = InetAddresses.forString(session.getBgpId().getValue()).getAddress();
331         this.peerId = RouterIds.createPeerId(session.getBgpId());
332         final KeyedInstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib
333                 .rev180329.bgp.rib.rib.Peer, PeerKey> peerIId =
334                 getInstanceIdentifier().child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns
335                 .yang.bgp.rib.rev180329.bgp.rib.rib.Peer.class, new PeerKey(this.peerId));
336         final Set<TablesKey> setTables = advertizedTableTypes.stream().map(t -> new TablesKey(t.getAfi(), t.getSafi()))
337                 .collect(Collectors.toSet());
338         this.tables = ImmutableSet.copyOf(setTables);
339         this.effRibInWriter = new EffectiveRibInWriter(this, this.rib,
340                 this.rib.createPeerChain(this),
341                 peerIId, this.tables, this.tableTypeRegistry,
342                 this.rtMemberships,
343                 this.rtCache);
344         registerPrefixesCounters(this.effRibInWriter, this.effRibInWriter);
345         this.peerRibOutIId = peerIId.child(AdjRibOut.class);
346         this.effRibInWriter.init();
347         setAdvertizedGracefulRestartTableTypes(advertizedGracefulRestartTableTypes.stream()
348                 .map(t -> new TablesKey(t.getAfi(), t.getSafi())).collect(Collectors.toList()));
349         this.addPathTableMaps = ImmutableMap.copyOf(mapTableTypesFamilies(addPathTablesType));
350         this.trackerRegistration = this.rib.getPeerTracker().registerPeer(this);
351
352         for (final TablesKey key : this.tables) {
353             createAdjRibOutListener(key, true);
354         }
355
356         addBgp4Support();
357
358         this.peerPath = createPeerPath();
359         this.ribWriter = this.ribWriter.transform(this.peerId, this.peerPath, this.rib.getRibSupportContext(),
360                 this.tables, this.addPathTableMaps);
361
362         if (this.rpcRegistry != null) {
363             this.rpcRegistration = this.rpcRegistry.addRoutedRpcImplementation(BgpPeerRpcService.class,
364                     new BgpPeerRpc(this, session, this.tables));
365             final KeyedInstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib
366                     .rev180329.bgp.rib.rib.Peer, PeerKey> path = this.rib.getInstanceIdentifier()
367                     .child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib
368                              .rib.Peer.class, new PeerKey(this.peerId));
369             this.rpcRegistration.registerPath(PeerContext.class, path);
370         }
371     }
372
373     //try to add a support for old-school BGP-4, if peer did not advertise IPv4-Unicast MP capability
374     private synchronized void addBgp4Support() {
375         final TablesKey key = new TablesKey(Ipv4AddressFamily.class, UnicastSubsequentAddressFamily.class);
376         if (!this.tables.contains(key)) {
377             final HashSet<TablesKey> newSet = new HashSet<>(this.tables);
378             newSet.add(key);
379             this.tables = ImmutableSet.copyOf(newSet);
380             createAdjRibOutListener(key, false);
381         }
382     }
383
384     private synchronized void createAdjRibOutListener(final TablesKey key,
385             final boolean mpSupport) {
386         final RIBSupport<?, ?, ?, ?> ribSupport = this.rib.getRibSupportContext().getRIBSupport(key);
387
388         // not particularly nice
389         if (ribSupport != null && this.session instanceof BGPSessionImpl) {
390             final ChannelOutputLimiter limiter = ((BGPSessionImpl) this.session).getLimiter();
391             final AdjRibOutListener adjRibOut = AdjRibOutListener.create(this.peerId, key,
392                     this.rib.getYangRibId(), this.rib.getCodecsRegistry(), ribSupport,
393                     this.rib.getService(), limiter, mpSupport);
394             this.adjRibOutListenerSet.put(key, adjRibOut);
395             registerPrefixesSentCounter(key, adjRibOut);
396         }
397     }
398
399     @Override
400     public synchronized void onSessionDown(final BGPSession session, final Exception e) {
401         if (e.getMessage().equals(BGPSessionImpl.END_OF_INPUT)) {
402             LOG.info("Session with peer {} went down", this.name);
403         } else {
404             LOG.info("Session with peer {} went down", this.name, e);
405         }
406         releaseConnection();
407     }
408
409     @Override
410     public synchronized void onSessionTerminated(final BGPSession session, final BGPTerminationReason cause) {
411         LOG.info("Session with peer {} terminated: {}", this.name, cause);
412         releaseConnection();
413     }
414
415     @Override
416     public String toString() {
417         return MoreObjects.toStringHelper(this)
418                 .add("name", this.name)
419                 .add("tables", this.tables).toString();
420         }
421
422     @Override
423     public synchronized FluentFuture<? extends CommitInfo> releaseConnection() {
424         LOG.info("Closing session with peer");
425         this.sessionUp = false;
426         this.adjRibOutListenerSet.values().forEach(AdjRibOutListener::close);
427         this.adjRibOutListenerSet.clear();
428         if (this.trackerRegistration != null) {
429             this.trackerRegistration.close();
430             this.trackerRegistration = null;
431         }
432         if (this.rpcRegistration != null) {
433             this.rpcRegistration.close();
434         }
435         releaseBindingChain();
436
437         this.ribWriter.releaseChain();
438         // FIXME: BUG-196: support graceful
439
440         if (this.effRibInWriter != null) {
441             this.effRibInWriter.close();
442         }
443         this.tables = Collections.emptySet();
444         this.addPathTableMaps = Collections.emptyMap();
445         final FluentFuture<? extends CommitInfo> future = removePeer(this.peerPath);
446         if (this.session != null) {
447             try {
448                 this.session.close();
449             } catch (final Exception e) {
450                 LOG.warn("Error closing session with peer", e);
451             }
452             this.session = null;
453         }
454         resetState();
455         return future;
456     }
457
458     @SuppressFBWarnings("IS2_INCONSISTENT_SYNC")
459     @Override
460     public SendReceive getSupportedAddPathTables(final TablesKey tableKey) {
461         return this.addPathTableMaps.get(tableKey);
462     }
463
464     @Override
465     public boolean supportsTable(final TablesKey tableKey) {
466         return this.tables.contains(tableKey) && this.sessionUp;
467     }
468
469     @Override
470     public KeyedInstanceIdentifier<Tables, TablesKey> getRibOutIId(final TablesKey tablesKey) {
471         return this.tablesIId.getUnchecked(tablesKey);
472     }
473
474     @Override
475     public synchronized void onTransactionChainFailed(final TransactionChain<?, ?> chain,
476             final AsyncTransaction<?, ?> transaction, final Throwable cause) {
477         LOG.error("Transaction domChain failed.", cause);
478         releaseConnection();
479     }
480
481     @Override
482     public synchronized void markUptodate(final TablesKey tablesKey) {
483         this.ribWriter.markTableUptodate(tablesKey);
484     }
485
486     @Override
487     public synchronized BGPSessionState getBGPSessionState() {
488         if (this.session instanceof BGPSessionStateProvider) {
489             return ((BGPSessionStateProvider) this.session).getBGPSessionState();
490         }
491         return null;
492     }
493
494     @Override
495     public synchronized BGPTimersState getBGPTimersState() {
496         if (this.session instanceof BGPSessionStateProvider) {
497             return ((BGPSessionStateProvider) this.session).getBGPTimersState();
498         }
499         return null;
500     }
501
502     @Override
503     public synchronized BGPTransportState getBGPTransportState() {
504         if (this.session instanceof BGPSessionStateProvider) {
505             return ((BGPSessionStateProvider) this.session).getBGPTransportState();
506         }
507         return null;
508     }
509
510     @Override
511     public List<RouteTarget> getMemberships() {
512         return this.rtMemberships;
513     }
514 }