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.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         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 = ImmutableMap.copyOf(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 }