Bug-2631 - Fixed BGP connection collision detection 84/14684/4
authorMilos Fabian <milfabia@cisco.com>
Tue, 3 Feb 2015 14:30:20 +0000 (15:30 +0100)
committerMilos Fabian <milfabia@cisco.com>
Sun, 8 Feb 2015 23:06:51 +0000 (23:06 +0000)
-have to examine all connections, in OpenConfirm state
-before, only fully established connections were examined
-connections are kept in map
-remove connection from map, when session goes down
-avoids https://bugs.opendaylight.org/show_bug.cgi?id=2109
-brings dependency of BGPSessionImpl on BGPPeerRegistry
-fixed bgp-identifier conversion to long

Change-Id: I700e8e5a0fb0874731ab2fe013cd7853e44428da
Signed-off-by: Milos Fabian <milfabia@cisco.com>
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AbstractBGPSessionNegotiator.java
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/BGPSessionImpl.java
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/StrictBGPPeerRegistry.java
bgp/rib-impl/src/test/java/org/opendaylight/protocol/bgp/rib/impl/ApplicationPeerTest.java
bgp/rib-impl/src/test/java/org/opendaylight/protocol/bgp/rib/impl/BGPDispatcherImplTest.java
bgp/rib-impl/src/test/java/org/opendaylight/protocol/bgp/rib/impl/BGPSessionImplTest.java
bgp/rib-impl/src/test/java/org/opendaylight/protocol/bgp/rib/impl/SpeakerSessionMock.java
bgp/rib-impl/src/test/java/org/opendaylight/protocol/bgp/rib/impl/StrictBGPPeerRegistryTest.java

index ba3085462b91549e311ec67573af5dd2a6e95a4a..99214bfd876ce1c13270be7218214c5a197783a6 100644 (file)
@@ -192,7 +192,7 @@ public abstract class AbstractBGPSessionNegotiator extends AbstractSessionNegoti
         try {
             final BGPSessionListener peer = this.registry.getPeer(getRemoteIp(), getSourceId(openObj, getPreferences()), getDestinationId(openObj, getPreferences()));
             this.sendMessage(new KeepaliveBuilder().build());
-            this.session = new BGPSessionImpl(peer, this.channel, openObj, getPreferences());
+            this.session = new BGPSessionImpl(peer, this.channel, openObj, getPreferences(), this.registry);
             this.state = State.OPEN_CONFIRM;
             LOG.debug("Channel {} moved to OpenConfirm state with remote proposal {}", this.channel, openObj);
         } catch (final BGPDocumentedException e) {
index 4c282f8fb5fc216515c27ed93be2cdc703d06b64..847be014c3a813d49a5923385a1828c595db755c 100644 (file)
@@ -25,6 +25,7 @@ import org.opendaylight.controller.config.yang.bgp.rib.impl.BgpSessionState;
 import org.opendaylight.protocol.bgp.parser.AsNumberUtil;
 import org.opendaylight.protocol.bgp.parser.BGPError;
 import org.opendaylight.protocol.bgp.parser.BgpTableTypeImpl;
+import org.opendaylight.protocol.bgp.rib.impl.spi.BGPPeerRegistry;
 import org.opendaylight.protocol.bgp.rib.impl.spi.BGPSessionPreferences;
 import org.opendaylight.protocol.bgp.rib.impl.spi.BGPSessionStatistics;
 import org.opendaylight.protocol.bgp.rib.spi.BGPSession;
@@ -105,20 +106,25 @@ public class BGPSessionImpl extends AbstractProtocolSession<Notification> implem
     private final int keepAlive;
     private final AsNumber asNumber;
     private final Ipv4Address bgpId;
+    private final BGPPeerRegistry peerRegistry;
+
     private BGPSessionStats sessionStats;
 
-    public BGPSessionImpl(final BGPSessionListener listener, final Channel channel, final Open remoteOpen, final BGPSessionPreferences localPreferences) {
-        this(listener, channel, remoteOpen, localPreferences.getHoldTime());
+    public BGPSessionImpl(final BGPSessionListener listener, final Channel channel, final Open remoteOpen, final BGPSessionPreferences localPreferences,
+            final BGPPeerRegistry peerRegitry) {
+        this(listener, channel, remoteOpen, localPreferences.getHoldTime(), peerRegitry);
         this.sessionStats = new BGPSessionStats(remoteOpen, this.holdTimerValue, this.keepAlive, channel, Optional.of(localPreferences), this.tableTypes);
     }
 
-    public BGPSessionImpl(final BGPSessionListener listener, final Channel channel, final Open remoteOpen, final int localHoldTimer) {
+    public BGPSessionImpl(final BGPSessionListener listener, final Channel channel, final Open remoteOpen, final int localHoldTimer,
+            final BGPPeerRegistry peerRegitry) {
         this.listener = Preconditions.checkNotNull(listener);
         this.channel = Preconditions.checkNotNull(channel);
         this.holdTimerValue = (remoteOpen.getHoldTimer() < localHoldTimer) ? remoteOpen.getHoldTimer() : localHoldTimer;
         LOG.info("BGP HoldTimer new value: {}", this.holdTimerValue);
         this.keepAlive = this.holdTimerValue / KA_TO_DEADTIMER_RATIO;
         this.asNumber = AsNumberUtil.advertizedAsNumber(remoteOpen);
+        this.peerRegistry = peerRegitry;
 
         final Set<TablesKey> tts = Sets.newHashSet();
         final Set<BgpTableType> tats = Sets.newHashSet();
@@ -163,9 +169,11 @@ public class BGPSessionImpl extends AbstractProtocolSession<Notification> implem
     @Override
     public synchronized void close() {
         LOG.info("Closing session: {}", this);
+
         if (this.state != State.IDLE) {
             this.sendMessage(new NotifyBuilder().setErrorCode(BGPError.CEASE.getCode()).setErrorSubcode(
                     BGPError.CEASE.getSubcode()).build());
+            removePeerSession();
             this.channel.close();
             this.state = State.IDLE;
         }
@@ -243,6 +251,7 @@ public class BGPSessionImpl extends AbstractProtocolSession<Notification> implem
 
     private synchronized void closeWithoutMessage() {
         LOG.debug("Closing session: {}", this);
+        removePeerSession();
         this.channel.close();
         this.state = State.IDLE;
     }
@@ -260,6 +269,12 @@ public class BGPSessionImpl extends AbstractProtocolSession<Notification> implem
         this.listener.onSessionTerminated(this, new BGPTerminationReason(error));
     }
 
+    private void removePeerSession() {
+        if (this.peerRegistry != null) {
+            this.peerRegistry.removePeerSession(StrictBGPPeerRegistry.getIpAddress(this.channel.remoteAddress()));
+        }
+    }
+
     /**
      * If HoldTimer expires, the session ends. If a message (whichever) was received during this period, the HoldTimer
      * will be rescheduled by HOLD_TIMER_VALUE + the time that has passed from the start of the HoldTimer to the time at
index d33e8be8b3b440b2753b12b96ff9530aeaab6f05..6eb975b120906671e5f431f6b6f4b187e0e26e14 100644 (file)
@@ -8,10 +8,11 @@
 
 package org.opendaylight.protocol.bgp.rib.impl;
 
-import com.google.common.base.CharMatcher;
 import com.google.common.base.Objects;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.Maps;
+import com.google.common.net.InetAddresses;
+import com.google.common.primitives.UnsignedInts;
 import java.net.Inet4Address;
 import java.net.Inet6Address;
 import java.net.InetAddress;
@@ -42,8 +43,6 @@ public final class StrictBGPPeerRegistry implements BGPPeerRegistry {
 
     private static final Logger LOG = LoggerFactory.getLogger(StrictBGPPeerRegistry.class);
 
-    private static final CharMatcher NONDIGIT = CharMatcher.inRange('0', '9').negate();
-
     // TODO remove backwards compatibility
     public static final StrictBGPPeerRegistry GLOBAL = new StrictBGPPeerRegistry();
 
@@ -68,6 +67,7 @@ public final class StrictBGPPeerRegistry implements BGPPeerRegistry {
         this.peers.remove(ip);
     }
 
+    @Override
     public synchronized void removePeerSession(final IpAddress ip) {
         Preconditions.checkNotNull(ip);
         this.sessionIds.remove(ip);
@@ -96,45 +96,41 @@ public final class StrictBGPPeerRegistry implements BGPPeerRegistry {
         final BGPSessionId currentConnection = new BGPSessionId(sourceId, remoteId);
         final BGPSessionListener p = this.peers.get(ip);
 
-        if (this.sessionIds.containsKey(ip)) {
-            if (p.isSessionActive()) {
-
-                LOG.warn("Duplicate BGP session established with {}", ip);
-
-                final BGPSessionId previousConnection = this.sessionIds.get(ip);
-
-                // Session reestablished with different ids
-                if (!previousConnection.equals(currentConnection)) {
-                    LOG.warn("BGP session with {} {} has to be dropped. Same session already present {}", ip, currentConnection, previousConnection);
-                    throw new BGPDocumentedException(
-                        String.format("BGP session with %s %s has to be dropped. Same session already present %s",
-                            ip, currentConnection, previousConnection),
-                            BGPError.CEASE);
-
-                    // Session reestablished with lower source bgp id, dropping current
-                } else if (previousConnection.isHigherDirection(currentConnection)) {
-                    LOG.warn("BGP session with {} {} has to be dropped. Opposite session already present", ip, currentConnection);
-                    throw new BGPDocumentedException(
-                        String.format("BGP session with %s initiated %s has to be dropped. Opposite session already present",
-                            ip, currentConnection),
-                            BGPError.CEASE);
-
-                    // Session reestablished with higher source bgp id, dropping previous
-                } else if (currentConnection.isHigherDirection(previousConnection)) {
-                    LOG.warn("BGP session with {} {} released. Replaced by opposite session", ip, previousConnection);
-                    this.peers.get(ip).releaseConnection();
-                    return this.peers.get(ip);
-
-                    // Session reestablished with same source bgp id, dropping current as duplicate
-                } else {
-                    LOG.warn("BGP session with %s initiated from %s to %s has to be dropped. Same session already present", ip, sourceId, remoteId);
-                    throw new BGPDocumentedException(
-                        String.format("BGP session with %s initiated %s has to be dropped. Same session already present",
-                            ip, currentConnection),
-                            BGPError.CEASE);
-                }
+        final BGPSessionId previousConnection = this.sessionIds.get(ip);
+
+        if (previousConnection != null) {
+
+            LOG.warn("Duplicate BGP session established with {}", ip);
+
+            // Session reestablished with different ids
+            if (!previousConnection.equals(currentConnection)) {
+                LOG.warn("BGP session with {} {} has to be dropped. Same session already present {}", ip, currentConnection, previousConnection);
+                throw new BGPDocumentedException(
+                    String.format("BGP session with %s %s has to be dropped. Same session already present %s",
+                        ip, currentConnection, previousConnection),
+                        BGPError.CEASE);
+
+                // Session reestablished with lower source bgp id, dropping current
+            } else if (previousConnection.isHigherDirection(currentConnection)) {
+                LOG.warn("BGP session with {} {} has to be dropped. Opposite session already present", ip, currentConnection);
+                throw new BGPDocumentedException(
+                    String.format("BGP session with %s initiated %s has to be dropped. Opposite session already present",
+                        ip, currentConnection),
+                        BGPError.CEASE);
+
+                // Session reestablished with higher source bgp id, dropping previous
+            } else if (currentConnection.isHigherDirection(previousConnection)) {
+                LOG.warn("BGP session with {} {} released. Replaced by opposite session", ip, previousConnection);
+                this.peers.get(ip).releaseConnection();
+                return this.peers.get(ip);
+
+                // Session reestablished with same source bgp id, dropping current as duplicate
             } else {
-                removePeerSession(ip);
+                LOG.warn("BGP session with %s initiated from %s to %s has to be dropped. Same session already present", ip, sourceId, remoteId);
+                throw new BGPDocumentedException(
+                    String.format("BGP session with %s initiated %s has to be dropped. Same session already present",
+                        ip, currentConnection),
+                        BGPError.CEASE);
             }
         }
 
@@ -187,6 +183,7 @@ public final class StrictBGPPeerRegistry implements BGPPeerRegistry {
      * Session identifier that contains (source Bgp Id) -> (destination Bgp Id)
      */
     private static final class BGPSessionId {
+
         private final Ipv4Address from, to;
 
         BGPSessionId(final Ipv4Address from, final Ipv4Address to) {
@@ -230,20 +227,12 @@ public final class StrictBGPPeerRegistry implements BGPPeerRegistry {
          * Check if this connection is equal to other and if it contains higher source bgp id
          */
         boolean isHigherDirection(final BGPSessionId other) {
-            Preconditions.checkState(!this.isSameDirection(other), "Equal sessions with same direction");
             return toLong(this.from) > toLong(other.from);
         }
 
         private long toLong(final Ipv4Address from) {
-            return Long.parseLong(NONDIGIT.removeFrom(from.getValue()));
-        }
-
-        /**
-         * Check if 2 connections are equal and face same direction
-         */
-        boolean isSameDirection(final BGPSessionId other) {
-            Preconditions.checkState(this.equals(other), "Only equal sessions can be compared");
-            return this.from.equals(other.from);
+            final int i = InetAddresses.coerceToInteger(InetAddresses.forString(from.getValue()));
+            return UnsignedInts.toLong(i);
         }
 
         @Override
index 68ee8ba3aedbfa07bc7e224f96158fc9fa09f16a..bf6741b6af49892b676a81b9ad1f15e0993970f7 100644 (file)
@@ -272,11 +272,12 @@ public class ApplicationPeerTest {
         Mockito.doReturn(null).when(this.eventLoop).schedule(any(Runnable.class), any(long.class), any(TimeUnit.class));
         Mockito.doReturn(Boolean.TRUE).when(this.channel).isWritable();
         Mockito.doReturn(null).when(this.channel).close();
+
         Mockito.doReturn(new InetSocketAddress("localhost", 12345)).when(this.channel).remoteAddress();
         Mockito.doReturn(new InetSocketAddress("localhost", 12345)).when(this.channel).localAddress();
         final List<BgpParameters> params = Lists.newArrayList(new BgpParametersBuilder().setOptionalCapabilities(Lists.newArrayList(new OptionalCapabilitiesBuilder().setCParameters(new MultiprotocolCaseBuilder()
             .setMultiprotocolCapability(new MultiprotocolCapabilityBuilder().setAfi(Ipv4AddressFamily.class).setSafi(UnicastSubsequentAddressFamily.class).build()).build()).build())).build());
-        this.session = new BGPSessionImpl(this.classic, this.channel, new OpenBuilder().setBgpIdentifier(new Ipv4Address("1.1.1.1")).setHoldTimer(50).setMyAsNumber(72).setBgpParameters(params).build(), 30);
+        this.session = new BGPSessionImpl(this.classic, this.channel, new OpenBuilder().setBgpIdentifier(new Ipv4Address("1.1.1.1")).setHoldTimer(50).setMyAsNumber(72).setBgpParameters(params).build(), 30, null);
         assertEquals(this.r, this.classic.getRib());
         assertEquals("testPeer", this.classic.getName());
         Mockito.doAnswer(new Answer<Object>() {
index 4b93060e5fb7b5052a7cd591ab0a802e7d491a7b..a32416645d6db9f2cf8fa61e037a291b862d8506 100644 (file)
@@ -85,6 +85,7 @@ public class BGPDispatcherImplTest {
         final BGPSessionPreferences prefs = new BGPSessionPreferences(AS_NUMBER, (short) 90, new Ipv4Address(ADDRESS.getAddress().getHostAddress()), tlvs);
         Mockito.doReturn(true).when(this.registry).isPeerConfigured(Mockito.any(IpAddress.class));
         Mockito.doReturn(prefs).when(this.registry).getPeerPreferences(Mockito.any(IpAddress.class));
+        Mockito.doNothing().when(this.registry).removePeerSession(Mockito.any(IpAddress.class));
         Mockito.doReturn(this.sessionListener).when(this.registry).getPeer(Mockito.any(IpAddress.class), Mockito.any(Ipv4Address.class), Mockito.any(Ipv4Address.class));
 
         this.dispatcher = new BGPDispatcherImpl(ServiceLoaderBGPExtensionProviderContext.getSingletonInstance().getMessageRegistry(), group, group);
index b47544aee6c90f3c74e9a0b43d75be3242d5c004..4cb633621021960264fc65b0f6483c8db0e392f8 100644 (file)
@@ -139,7 +139,7 @@ public class BGPSessionImplTest {
         doReturn(this.pipeline).when(this.pipeline).replace(any(ChannelHandler.class), any(String.class), any(ChannelHandler.class));
         doReturn(mock(ChannelFuture.class)).when(this.speakerListener).close();
         this.listener = new SimpleSessionListener();
-        this.bgpSession = new BGPSessionImpl(this.listener, this.speakerListener, this.classicOpen, this.classicOpen.getHoldTimer());
+        this.bgpSession = new BGPSessionImpl(this.listener, this.speakerListener, this.classicOpen, this.classicOpen.getHoldTimer(), null);
     }
 
     @Test
index 72bdf15dcec517accff6f78a2bd0307b639fa7dc..09ef537bf0a685116da01c368235ff6cd4fc7d75 100644 (file)
@@ -22,7 +22,7 @@ public class SpeakerSessionMock extends BGPSessionImpl {
     private final BGPSessionListener client;
 
     SpeakerSessionMock(final BGPSessionListener listener, final BGPSessionListener client) {
-        super(listener, mock(Channel.class), new OpenBuilder().setHoldTimer(5).build(), 10);
+        super(listener, mock(Channel.class), new OpenBuilder().setHoldTimer(5).build(), 10, null);
         this.client = client;
     }
 
index 004ab5dada0a332dbf249320ccb0d960696743e1..cee7899dd43c380e7b1de3375fd2cca82b91c311 100644 (file)
@@ -17,6 +17,7 @@ import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mockito;
 import org.opendaylight.protocol.bgp.parser.BGPDocumentedException;
+import org.opendaylight.protocol.bgp.parser.BGPError;
 import org.opendaylight.protocol.bgp.rib.impl.spi.BGPSessionPreferences;
 import org.opendaylight.protocol.bgp.rib.impl.spi.ReusableBGPPeer;
 import org.opendaylight.protocol.bgp.rib.spi.BGPSessionListener;
@@ -53,8 +54,8 @@ public class StrictBGPPeerRegistryTest {
         this.droppingBGPSessionRegistry.getPeer(remoteIp, from, to);
         try {
             this.droppingBGPSessionRegistry.getPeer(remoteIp, from, to);
-        } catch (final IllegalStateException e) {
-            Mockito.verify(session1).isSessionActive();
+        } catch (final BGPDocumentedException e) {
+            assertEquals(BGPError.CEASE, e.getError());
             return;
         }
 
@@ -111,7 +112,7 @@ public class StrictBGPPeerRegistryTest {
         try {
             this.droppingBGPSessionRegistry.getPeer(remoteIp, lower, higher);
         } catch (final BGPDocumentedException e) {
-            Mockito.verify(session1).isSessionActive();
+            assertEquals(BGPError.CEASE, e.getError());
             return;
         }
 
@@ -132,10 +133,29 @@ public class StrictBGPPeerRegistryTest {
         Mockito.verify(session1).releaseConnection();
     }
 
+    @Test
+    public void testDuplicateDifferentIds() throws Exception {
+        final Ipv4Address from = new Ipv4Address("0.0.0.1");
+        final IpAddress remoteIp = new IpAddress(from);
+        final Ipv4Address to = new Ipv4Address("255.255.255.255");
+
+        final ReusableBGPPeer session1 = getMockSession();
+        this.droppingBGPSessionRegistry.addPeer(remoteIp, session1, this.mockPreferences);
+
+        this.droppingBGPSessionRegistry.getPeer(remoteIp, from, to);
+        try {
+            this.droppingBGPSessionRegistry.getPeer(remoteIp, to, to);
+        } catch (final BGPDocumentedException e) {
+            assertEquals(BGPError.CEASE, e.getError());
+            return;
+        }
+
+        fail("Same peer cannot be connected twice");
+    }
+
     private ReusableBGPPeer getMockSession() {
         final ReusableBGPPeer mock = Mockito.mock(ReusableBGPPeer.class);
         Mockito.doNothing().when(mock).releaseConnection();
-        Mockito.doReturn(Boolean.TRUE).when(mock).isSessionActive();
         return mock;
     }