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