remove redundant ImmutableMap call
[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) && !(msg instanceof RouteRefresh)) {
241             LOG.info("Ignoring unhandled message class {}", msg.getClass());
242             return;
243         }
244         if (msg instanceof Update) {
245             onUpdateMessage((Update) msg);
246         } else {
247             onRouteRefreshMessage((RouteRefresh) msg);
248         }
249     }
250
251     private void onRouteRefreshMessage(final RouteRefresh message) {
252         final Class<? extends AddressFamily> rrAfi = message.getAfi();
253         final Class<? extends SubsequentAddressFamily> rrSafi = message.getSafi();
254
255         final TablesKey key = new TablesKey(rrAfi, rrSafi);
256         final AdjRibOutListener listener = this.adjRibOutListenerSet.get(key);
257         if (listener != null) {
258             listener.close();
259             this.adjRibOutListenerSet.remove(key);
260             createAdjRibOutListener(key, listener.isMpSupported());
261         } else {
262             LOG.info("Ignoring RouteRefresh message. Afi/Safi is not supported: {}, {}.", rrAfi, rrSafi);
263         }
264     }
265
266     /**
267      * Check for presence of well known mandatory attribute LOCAL_PREF in Update message.
268      *
269      * @param message Update message
270      */
271     private void checkMandatoryAttributesPresence(final Update message) throws BGPDocumentedException {
272         if (MessageUtil.isAnyNlriPresent(message)) {
273             final Attributes attrs = message.getAttributes();
274             if (this.peerRole == PeerRole.Ibgp && (attrs == null || attrs.getLocalPref() == null)) {
275                 throw new BGPDocumentedException(BGPError.MANDATORY_ATTR_MISSING_MSG + "LOCAL_PREF",
276                         BGPError.WELL_KNOWN_ATTR_MISSING,
277                         new byte[]{LocalPreferenceAttributeParser.TYPE});
278             }
279         }
280     }
281
282     /**
283      * Process Update message received.
284      * Calls {@link #checkMandatoryAttributesPresence(Update)} to check for presence of mandatory attributes.
285      *
286      * @param message Update message
287      */
288     private synchronized void onUpdateMessage(final Update message) throws BGPDocumentedException {
289         checkMandatoryAttributesPresence(message);
290
291         // update AdjRibs
292         final Attributes attrs = message.getAttributes();
293         MpReachNlri mpReach;
294         final boolean isAnyNlriAnnounced = message.getNlri() != null;
295         if (isAnyNlriAnnounced) {
296             mpReach = prefixesToMpReach(message);
297         } else {
298             mpReach = MessageUtil.getMpReachNlri(attrs);
299         }
300         if (mpReach != null) {
301             this.ribWriter.updateRoutes(mpReach, nextHopToAttribute(attrs, mpReach));
302         }
303         MpUnreachNlri mpUnreach;
304         if (message.getWithdrawnRoutes() != null) {
305             mpUnreach = prefixesToMpUnreach(message, isAnyNlriAnnounced);
306         } else {
307             mpUnreach = MessageUtil.getMpUnreachNlri(attrs);
308         }
309         if (mpUnreach != null) {
310             this.ribWriter.removeRoutes(mpUnreach);
311         }
312     }
313
314     @Override
315     public synchronized void onSessionUp(final BGPSession session) {
316         this.session = session;
317         this.sessionUp = true;
318         this.bindingChain = this.rib.createPeerChain(this);
319         if (this.session instanceof BGPSessionStateProvider) {
320             ((BGPSessionStateProvider) this.session).registerMessagesCounter(this);
321         }
322
323         final List<AddressFamilies> addPathTablesType = session.getAdvertisedAddPathTableTypes();
324         final Set<BgpTableType> advertizedTableTypes = session.getAdvertisedTableTypes();
325         final List<BgpTableType> advertizedGracefulRestartTableTypes = session.getAdvertisedGracefulRestartTableTypes();
326         LOG.info("Session with peer {} went up with tables {} and Add Path tables {}", this.name,
327                 advertizedTableTypes, addPathTablesType);
328         this.rawIdentifier = InetAddresses.forString(session.getBgpId().getValue()).getAddress();
329         this.peerId = RouterIds.createPeerId(session.getBgpId());
330         final KeyedInstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib
331                 .rev180329.bgp.rib.rib.Peer, PeerKey> peerIId =
332                 getInstanceIdentifier().child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns
333                 .yang.bgp.rib.rev180329.bgp.rib.rib.Peer.class, new PeerKey(this.peerId));
334         final Set<TablesKey> setTables = advertizedTableTypes.stream().map(t -> new TablesKey(t.getAfi(), t.getSafi()))
335                 .collect(Collectors.toSet());
336         this.tables = ImmutableSet.copyOf(setTables);
337         this.effRibInWriter = new EffectiveRibInWriter(this, this.rib,
338                 this.rib.createPeerChain(this),
339                 peerIId, this.tables, this.tableTypeRegistry,
340                 this.rtMemberships,
341                 this.rtCache);
342         registerPrefixesCounters(this.effRibInWriter, this.effRibInWriter);
343         this.peerRibOutIId = peerIId.child(AdjRibOut.class);
344         this.effRibInWriter.init();
345         setAdvertizedGracefulRestartTableTypes(advertizedGracefulRestartTableTypes.stream()
346                 .map(t -> new TablesKey(t.getAfi(), t.getSafi())).collect(Collectors.toList()));
347         this.addPathTableMaps = mapTableTypesFamilies(addPathTablesType);
348         this.trackerRegistration = this.rib.getPeerTracker().registerPeer(this);
349
350         for (final TablesKey key : this.tables) {
351             createAdjRibOutListener(key, true);
352         }
353
354         addBgp4Support();
355
356         this.peerPath = createPeerPath();
357         this.ribWriter = this.ribWriter.transform(this.peerId, this.peerPath, this.rib.getRibSupportContext(),
358                 this.tables, this.addPathTableMaps);
359
360         if (this.rpcRegistry != null) {
361             this.rpcRegistration = this.rpcRegistry.addRoutedRpcImplementation(BgpPeerRpcService.class,
362                     new BgpPeerRpc(this, session, this.tables));
363             final KeyedInstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib
364                     .rev180329.bgp.rib.rib.Peer, PeerKey> path = this.rib.getInstanceIdentifier()
365                     .child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib
366                              .rib.Peer.class, new PeerKey(this.peerId));
367             this.rpcRegistration.registerPath(PeerContext.class, path);
368         }
369     }
370
371     //try to add a support for old-school BGP-4, if peer did not advertise IPv4-Unicast MP capability
372     private synchronized void addBgp4Support() {
373         final TablesKey key = new TablesKey(Ipv4AddressFamily.class, UnicastSubsequentAddressFamily.class);
374         if (!this.tables.contains(key)) {
375             final HashSet<TablesKey> newSet = new HashSet<>(this.tables);
376             newSet.add(key);
377             this.tables = ImmutableSet.copyOf(newSet);
378             createAdjRibOutListener(key, false);
379         }
380     }
381
382     private synchronized void createAdjRibOutListener(final TablesKey key,
383             final boolean mpSupport) {
384         final RIBSupport<?, ?, ?, ?> ribSupport = this.rib.getRibSupportContext().getRIBSupport(key);
385
386         // not particularly nice
387         if (ribSupport != null && this.session instanceof BGPSessionImpl) {
388             final ChannelOutputLimiter limiter = ((BGPSessionImpl) this.session).getLimiter();
389             final AdjRibOutListener adjRibOut = AdjRibOutListener.create(this.peerId, key,
390                     this.rib.getYangRibId(), this.rib.getCodecsRegistry(), ribSupport,
391                     this.rib.getService(), limiter, mpSupport);
392             this.adjRibOutListenerSet.put(key, adjRibOut);
393             registerPrefixesSentCounter(key, adjRibOut);
394         }
395     }
396
397     @Override
398     public synchronized void onSessionDown(final BGPSession session, final Exception e) {
399         if (e.getMessage().equals(BGPSessionImpl.END_OF_INPUT)) {
400             LOG.info("Session with peer {} went down", this.name);
401         } else {
402             LOG.info("Session with peer {} went down", this.name, e);
403         }
404         releaseConnection();
405     }
406
407     @Override
408     public synchronized void onSessionTerminated(final BGPSession session, final BGPTerminationReason cause) {
409         LOG.info("Session with peer {} terminated: {}", this.name, cause);
410         releaseConnection();
411     }
412
413     @Override
414     public String toString() {
415         return MoreObjects.toStringHelper(this)
416                 .add("name", this.name)
417                 .add("tables", this.tables).toString();
418         }
419
420     @Override
421     public synchronized FluentFuture<? extends CommitInfo> releaseConnection() {
422         LOG.info("Closing session with peer");
423         this.sessionUp = false;
424         this.adjRibOutListenerSet.values().forEach(AdjRibOutListener::close);
425         this.adjRibOutListenerSet.clear();
426         if (this.trackerRegistration != null) {
427             this.trackerRegistration.close();
428             this.trackerRegistration = null;
429         }
430         if (this.rpcRegistration != null) {
431             this.rpcRegistration.close();
432         }
433         releaseBindingChain();
434
435         this.ribWriter.releaseChain();
436         // FIXME: BUG-196: support graceful
437
438         if (this.effRibInWriter != null) {
439             this.effRibInWriter.close();
440         }
441         this.tables = Collections.emptySet();
442         this.addPathTableMaps = Collections.emptyMap();
443         final FluentFuture<? extends CommitInfo> future = removePeer(this.peerPath);
444         if (this.session != null) {
445             try {
446                 this.session.close();
447             } catch (final Exception e) {
448                 LOG.warn("Error closing session with peer", e);
449             }
450             this.session = null;
451         }
452         resetState();
453         return future;
454     }
455
456     @SuppressFBWarnings("IS2_INCONSISTENT_SYNC")
457     @Override
458     public SendReceive getSupportedAddPathTables(final TablesKey tableKey) {
459         return this.addPathTableMaps.get(tableKey);
460     }
461
462     @Override
463     public boolean supportsTable(final TablesKey tableKey) {
464         return this.tables.contains(tableKey) && this.sessionUp;
465     }
466
467     @Override
468     public KeyedInstanceIdentifier<Tables, TablesKey> getRibOutIId(final TablesKey tablesKey) {
469         return this.tablesIId.getUnchecked(tablesKey);
470     }
471
472     @Override
473     public synchronized void onTransactionChainFailed(final TransactionChain<?, ?> chain,
474             final AsyncTransaction<?, ?> transaction, final Throwable cause) {
475         LOG.error("Transaction domChain failed.", cause);
476         releaseConnection();
477     }
478
479     @Override
480     public synchronized void markUptodate(final TablesKey tablesKey) {
481         this.ribWriter.markTableUptodate(tablesKey);
482     }
483
484     @Override
485     public synchronized BGPSessionState getBGPSessionState() {
486         if (this.session instanceof BGPSessionStateProvider) {
487             return ((BGPSessionStateProvider) this.session).getBGPSessionState();
488         }
489         return null;
490     }
491
492     @Override
493     public synchronized BGPTimersState getBGPTimersState() {
494         if (this.session instanceof BGPSessionStateProvider) {
495             return ((BGPSessionStateProvider) this.session).getBGPTimersState();
496         }
497         return null;
498     }
499
500     @Override
501     public synchronized BGPTransportState getBGPTransportState() {
502         if (this.session instanceof BGPSessionStateProvider) {
503             return ((BGPSessionStateProvider) this.session).getBGPTransportState();
504         }
505         return null;
506     }
507
508     @Override
509     public List<RouteTarget> getMemberships() {
510         return this.rtMemberships;
511     }
512 }