Split Peer Ribout storage from loc rib
[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.SubsequentAddressFamily;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.UnicastSubsequentAddressFamily;
87 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
88 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
89 import org.opendaylight.yangtools.yang.binding.Notification;
90 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
91 import org.slf4j.Logger;
92 import org.slf4j.LoggerFactory;
93
94 /**
95  * Class representing a peer. We have a single instance for each peer, which provides translation from BGP events into
96  * RIB actions.
97  */
98 public class BGPPeer extends AbstractPeer implements BGPSessionListener {
99     private static final Logger LOG = LoggerFactory.getLogger(BGPPeer.class);
100
101     private Set<TablesKey> tables = Collections.emptySet();
102     private final RIB rib;
103     private final Map<TablesKey, AdjRibOutListener> adjRibOutListenerSet = new HashMap<>();
104     private final RpcProviderRegistry rpcRegistry;
105     private final BGPTableTypeRegistryConsumer tableTypeRegistry;
106     private InstanceIdentifier<AdjRibOut> peerRibOutIId;
107     @GuardedBy("this")
108     private AbstractRegistration trackerRegistration;
109     private final LoadingCache<TablesKey, KeyedInstanceIdentifier<Tables, TablesKey>> tablesIId
110             = CacheBuilder.newBuilder()
111             .build(new CacheLoader<TablesKey, KeyedInstanceIdentifier<Tables, TablesKey>>() {
112                 @Override
113                 public KeyedInstanceIdentifier<Tables, TablesKey> load(final TablesKey tablesKey) {
114                     return BGPPeer.this.peerRibOutIId.child(Tables.class, tablesKey);
115                 }
116             });
117
118     @GuardedBy("this")
119     private BGPSession session;
120     @GuardedBy("this")
121     private AdjRibInWriter ribWriter;
122     @GuardedBy("this")
123     private EffectiveRibInWriter effRibInWriter;
124     private RoutedRpcRegistration<BgpPeerRpcService> rpcRegistration;
125     private Map<TablesKey, SendReceive> addPathTableMaps = Collections.emptyMap();
126     private YangInstanceIdentifier peerPath;
127     private boolean sessionUp;
128
129     public BGPPeer(
130             final BGPTableTypeRegistryConsumer tableTypeRegistry,
131             final IpAddress neighborAddress,
132             final String peerGroupName,
133             final RIB rib,
134             final PeerRole role,
135             final ClusterIdentifier clusterId,
136             final AsNumber localAs,
137             final RpcProviderRegistry rpcRegistry,
138             final Set<TablesKey> afiSafisAdvertized,
139             final Set<TablesKey> afiSafisGracefulAdvertized) {
140         super(rib, Ipv4Util.toStringIP(neighborAddress), peerGroupName, role, clusterId,
141                 localAs, neighborAddress, afiSafisAdvertized, afiSafisGracefulAdvertized);
142         this.tableTypeRegistry = requireNonNull(tableTypeRegistry);
143         this.rib = requireNonNull(rib);
144         this.rpcRegistry = rpcRegistry;
145     }
146
147     BGPPeer(
148             final BGPTableTypeRegistryConsumer tableTypeRegistry,
149             final IpAddress neighborAddress,
150             final RIB rib,
151             final PeerRole role,
152             final RpcProviderRegistry rpcRegistry,
153             final Set<TablesKey> afiSafisAdvertized,
154             final Set<TablesKey> afiSafisGracefulAdvertized) {
155         this(tableTypeRegistry, neighborAddress, null, rib, role, null, null, rpcRegistry,
156                 afiSafisAdvertized, afiSafisGracefulAdvertized);
157     }
158
159
160     private static Attributes nextHopToAttribute(final Attributes attrs, final MpReachNlri mpReach) {
161         if (attrs.getCNextHop() == null && mpReach.getCNextHop() != null) {
162             final AttributesBuilder attributesBuilder = new AttributesBuilder(attrs);
163             attributesBuilder.setCNextHop(mpReach.getCNextHop());
164             return attributesBuilder.build();
165         }
166         return attrs;
167     }
168
169     /**
170      * Creates MPReach for the prefixes to be handled in the same way as linkstate routes.
171      *
172      * @param message Update message containing prefixes in NLRI
173      * @return MpReachNlri with prefixes from the nlri field
174      */
175     private static MpReachNlri prefixesToMpReach(final Update message) {
176         final List<Ipv4Prefixes> prefixes = message.getNlri().stream()
177                 .map(n -> new Ipv4PrefixesBuilder().setPrefix(n.getPrefix()).setPathId(n.getPathId()).build())
178                 .collect(Collectors.toList());
179         final MpReachNlriBuilder b = new MpReachNlriBuilder().setAfi(Ipv4AddressFamily.class).setSafi(
180                 UnicastSubsequentAddressFamily.class).setAdvertizedRoutes(
181                 new AdvertizedRoutesBuilder().setDestinationType(
182                         new DestinationIpv4CaseBuilder().setDestinationIpv4(
183                                 new DestinationIpv4Builder().setIpv4Prefixes(prefixes).build()).build()).build());
184         if (message.getAttributes() != null) {
185             b.setCNextHop(message.getAttributes().getCNextHop());
186         }
187         return b.build();
188     }
189
190     /**
191      * Create MPUnreach for the prefixes to be handled in the same way as linkstate routes.
192      *
193      * @param message            Update message containing withdrawn routes
194      * @param isAnyNlriAnnounced isAnyNlriAnnounced
195      * @return MpUnreachNlri with prefixes from the withdrawn routes field
196      */
197     private static MpUnreachNlri prefixesToMpUnreach(final Update message, final boolean isAnyNlriAnnounced) {
198         final List<Ipv4Prefixes> prefixes = new ArrayList<>();
199         message.getWithdrawnRoutes().forEach(w -> {
200
201             Optional<Nlri> nlriAnounced = Optional.empty();
202             if (isAnyNlriAnnounced) {
203                 nlriAnounced = message.getNlri().stream().filter(n -> Objects.equal(n.getPrefix(), w.getPrefix())
204                         && Objects.equal(n.getPathId(), w.getPathId()))
205                         .findAny();
206             }
207             if (!nlriAnounced.isPresent()) {
208                 prefixes.add(new Ipv4PrefixesBuilder().setPrefix(w.getPrefix()).setPathId(w.getPathId()).build());
209             }
210         });
211         return new MpUnreachNlriBuilder().setAfi(Ipv4AddressFamily.class).setSafi(UnicastSubsequentAddressFamily.class)
212                 .setWithdrawnRoutes(new WithdrawnRoutesBuilder().setDestinationType(new org.opendaylight.yang.gen.v1
213                         .urn.opendaylight.params.xml.ns.yang.bgp.inet.rev180329.update.attributes.mp.unreach.nlri
214                         .withdrawn.routes.destination.type.DestinationIpv4CaseBuilder().setDestinationIpv4(
215                         new DestinationIpv4Builder().setIpv4Prefixes(prefixes).build()).build()).build()).build();
216     }
217
218     private static Map<TablesKey, SendReceive> mapTableTypesFamilies(final List<AddressFamilies> addPathTablesType) {
219         return ImmutableMap.copyOf(addPathTablesType.stream().collect(Collectors.toMap(af -> new TablesKey(af.getAfi(),
220                         af.getSafi()), BgpAddPathTableType::getSendReceive)));
221     }
222
223     public synchronized void instantiateServiceInstance() {
224         this.ribWriter = AdjRibInWriter.create(this.rib.getYangRibId(), this.peerRole, this);
225         setActive(true);
226     }
227
228     @Override
229     public synchronized FluentFuture<? extends CommitInfo> close() {
230         final FluentFuture<? extends CommitInfo> future = releaseConnection();
231         closeDomChain();
232         setActive(false);
233         return future;
234     }
235
236     @Override
237     public void onMessage(final BGPSession session, final Notification msg) throws BGPDocumentedException {
238         if (!(msg instanceof Update) && !(msg instanceof RouteRefresh)) {
239             LOG.info("Ignoring unhandled message class {}", msg.getClass());
240             return;
241         }
242         if (msg instanceof Update) {
243             onUpdateMessage((Update) msg);
244         } else {
245             onRouteRefreshMessage((RouteRefresh) msg);
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         registerPrefixesCounters(this.effRibInWriter, this.effRibInWriter);
339         this.peerRibOutIId = peerIId.child(AdjRibOut.class);
340         this.effRibInWriter.init();
341         setAdvertizedGracefulRestartTableTypes(advertizedGracefulRestartTableTypes.stream()
342                 .map(t -> new TablesKey(t.getAfi(), t.getSafi())).collect(Collectors.toList()));
343         this.addPathTableMaps = ImmutableMap.copyOf(mapTableTypesFamilies(addPathTablesType));
344         this.trackerRegistration = this.rib.getPeerTracker().registerPeer(this);
345
346         for (final TablesKey key : this.tables) {
347             createAdjRibOutListener(key, true);
348         }
349
350         addBgp4Support();
351
352         this.peerPath = createPeerPath();
353         this.ribWriter = this.ribWriter.transform(this.peerId, this.peerPath, this.rib.getRibSupportContext(),
354                 this.tables, this.addPathTableMaps);
355
356         if (this.rpcRegistry != null) {
357             this.rpcRegistration = this.rpcRegistry.addRoutedRpcImplementation(BgpPeerRpcService.class,
358                     new BgpPeerRpc(this, session, this.tables));
359             final KeyedInstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib
360                     .rev180329.bgp.rib.rib.Peer, PeerKey> path = this.rib.getInstanceIdentifier()
361                     .child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib
362                              .rib.Peer.class, new PeerKey(this.peerId));
363             this.rpcRegistration.registerPath(PeerContext.class, path);
364         }
365     }
366
367     //try to add a support for old-school BGP-4, if peer did not advertise IPv4-Unicast MP capability
368     private synchronized void addBgp4Support() {
369         final TablesKey key = new TablesKey(Ipv4AddressFamily.class, UnicastSubsequentAddressFamily.class);
370         if (!this.tables.contains(key)) {
371             final HashSet<TablesKey> newSet = new HashSet<>(this.tables);
372             newSet.add(key);
373             this.tables = ImmutableSet.copyOf(newSet);
374             createAdjRibOutListener(key, false);
375         }
376     }
377
378     private synchronized void createAdjRibOutListener(final TablesKey key,
379             final boolean mpSupport) {
380         final RIBSupport<?, ?, ?, ?> ribSupport = this.rib.getRibSupportContext().getRIBSupport(key);
381
382         // not particularly nice
383         if (ribSupport != null && this.session instanceof BGPSessionImpl) {
384             final ChannelOutputLimiter limiter = ((BGPSessionImpl) this.session).getLimiter();
385             final AdjRibOutListener adjRibOut = AdjRibOutListener.create(this.peerId, key,
386                     this.rib.getYangRibId(), this.rib.getCodecsRegistry(), ribSupport,
387                     this.rib.getService(), limiter, mpSupport);
388             this.adjRibOutListenerSet.put(key, adjRibOut);
389             registerPrefixesSentCounter(key, adjRibOut);
390         }
391     }
392
393     @Override
394     public synchronized void onSessionDown(final BGPSession session, final Exception e) {
395         if (e.getMessage().equals(BGPSessionImpl.END_OF_INPUT)) {
396             LOG.info("Session with peer {} went down", this.name);
397         } else {
398             LOG.info("Session with peer {} went down", this.name, e);
399         }
400         releaseConnection();
401     }
402
403     @Override
404     public synchronized void onSessionTerminated(final BGPSession session, final BGPTerminationReason cause) {
405         LOG.info("Session with peer {} terminated: {}", this.name, cause);
406         releaseConnection();
407     }
408
409     @Override
410     public String toString() {
411         return MoreObjects.toStringHelper(this)
412                 .add("name", this.name)
413                 .add("tables", this.tables).toString();
414         }
415
416     @Override
417     public synchronized FluentFuture<? extends CommitInfo> releaseConnection() {
418         LOG.info("Closing session with peer");
419         this.sessionUp = false;
420         this.adjRibOutListenerSet.values().forEach(AdjRibOutListener::close);
421         this.adjRibOutListenerSet.clear();
422         if (this.trackerRegistration != null) {
423             this.trackerRegistration.close();
424             this.trackerRegistration = null;
425         }
426         if (this.rpcRegistration != null) {
427             this.rpcRegistration.close();
428         }
429         releaseBindingChain();
430
431         this.ribWriter.releaseChain();
432         // FIXME: BUG-196: support graceful
433
434         if (this.effRibInWriter != null) {
435             this.effRibInWriter.close();
436         }
437         this.tables = Collections.emptySet();
438         this.addPathTableMaps = Collections.emptyMap();
439         final FluentFuture<? extends CommitInfo> future = removePeer(this.peerPath);
440         if (this.session != null) {
441             try {
442                 this.session.close();
443             } catch (final Exception e) {
444                 LOG.warn("Error closing session with peer", e);
445             }
446             this.session = null;
447         }
448         resetState();
449         return future;
450     }
451
452     @SuppressFBWarnings("IS2_INCONSISTENT_SYNC")
453     @Override
454     public SendReceive getSupportedAddPathTables(final TablesKey tableKey) {
455         return this.addPathTableMaps.get(tableKey);
456     }
457
458     @Override
459     public boolean supportsTable(final TablesKey tableKey) {
460         return this.tables.contains(tableKey) && this.sessionUp;
461     }
462
463     @Override
464     public KeyedInstanceIdentifier<Tables, TablesKey> getRibOutIId(final TablesKey tablesKey) {
465         return this.tablesIId.getUnchecked(tablesKey);
466     }
467
468     @Override
469     public synchronized void onTransactionChainFailed(final TransactionChain<?, ?> chain,
470             final AsyncTransaction<?, ?> transaction, final Throwable cause) {
471         LOG.error("Transaction domChain failed.", cause);
472         releaseConnection();
473     }
474
475     @Override
476     public synchronized void markUptodate(final TablesKey tablesKey) {
477         this.ribWriter.markTableUptodate(tablesKey);
478     }
479
480     @Override
481     public synchronized BGPSessionState getBGPSessionState() {
482         if (this.session instanceof BGPSessionStateProvider) {
483             return ((BGPSessionStateProvider) this.session).getBGPSessionState();
484         }
485         return null;
486     }
487
488     @Override
489     public synchronized BGPTimersState getBGPTimersState() {
490         if (this.session instanceof BGPSessionStateProvider) {
491             return ((BGPSessionStateProvider) this.session).getBGPTimersState();
492         }
493         return null;
494     }
495
496     @Override
497     public synchronized BGPTransportState getBGPTransportState() {
498         if (this.session instanceof BGPSessionStateProvider) {
499             return ((BGPSessionStateProvider) this.session).getBGPTransportState();
500         }
501         return null;
502     }
503 }