BUG-338 Allow incomming BGP connections. 95/8495/10
authorMaros Marsalek <mmarsale@cisco.com>
Fri, 4 Jul 2014 13:05:06 +0000 (15:05 +0200)
committerMaros Marsalek <mmarsale@cisco.com>
Tue, 8 Jul 2014 13:58:36 +0000 (15:58 +0200)
Added BGPPeerRegistry that holds all configured BGP peers.
Client/Server SessionNegotiators retrieve BGPPeers(SessionListeners) from the BGPPeerRegistry.
Added StrictBGPPeerRegistry (default implementation of BGPPeerRegistry) that allows only 1 session with BGP peer. If second session is being established, the registry drops either already established or new BGP session (depending on higher source BGP ID).

Added 2 config attributes to BGPPeer:
- initiate connection: true to start BGP connection from ODL. False if just configure BGP peer to accept session from remote device.
- peer registry: dependency on BGPPeerRegistry. Peer registers itself into this registry.

* new attributes are optional to preserve backwards compatibility.

Change-Id: I2f7a606db5196fed49094d49efbb570c4d0fbecb
Signed-off-by: Maros Marsalek <mmarsale@cisco.com>
34 files changed:
bgp/controller-config/src/main/resources/initial/31-bgp.xml
bgp/controller-config/src/main/resources/initial/41-bgp-example.xml
bgp/parser-api/src/main/java/org/opendaylight/protocol/bgp/parser/BGPTerminationReason.java
bgp/rib-impl-config/src/main/java/org/opendaylight/controller/config/yang/bgp/rib/impl/BGPPeerAcceptorModule.java [new file with mode: 0644]
bgp/rib-impl-config/src/main/java/org/opendaylight/controller/config/yang/bgp/rib/impl/BGPPeerAcceptorModuleFactory.java [new file with mode: 0644]
bgp/rib-impl-config/src/main/java/org/opendaylight/controller/config/yang/bgp/rib/impl/BGPPeerModule.java
bgp/rib-impl-config/src/main/java/org/opendaylight/controller/config/yang/bgp/rib/impl/StrictBgpPeerRegistryModule.java [new file with mode: 0644]
bgp/rib-impl-config/src/main/java/org/opendaylight/controller/config/yang/bgp/rib/impl/StrictBgpPeerRegistryModuleFactory.java [new file with mode: 0644]
bgp/rib-impl-config/src/main/yang/bgp-rib-impl.yang
bgp/rib-impl-config/src/test/java/org/opendaylight/controller/config/yang/bgp/rib/impl/BGPPeerAcceptorModuleTest.java [new file with mode: 0644]
bgp/rib-impl-config/src/test/java/org/opendaylight/controller/config/yang/bgp/rib/impl/BGPPeerModuleTest.java
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AbstractBGPSessionNegotiator.java [moved from bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/BGPSessionNegotiator.java with 60% similarity]
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/BGPDispatcherImpl.java
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/BGPPeer.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 [new file with mode: 0644]
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/client/BGPClientSessionNegotiator.java [new file with mode: 0644]
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/client/BGPClientSessionNegotiatorFactory.java [moved from bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/BGPSessionNegotiatorFactory.java with 60% similarity]
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/client/BGPClientSessionValidator.java [new file with mode: 0644]
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/server/BGPServerSessionNegotiator.java [new file with mode: 0644]
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/server/BGPServerSessionNegotiatorFactory.java [new file with mode: 0644]
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/server/BGPServerSessionValidator.java [new file with mode: 0644]
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/spi/BGPDispatcher.java
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/spi/BGPPeerRegistry.java [new file with mode: 0644]
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/spi/BGPServerDispatcher.java [new file with mode: 0644]
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/spi/BGPSessionValidator.java [new file with mode: 0644]
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/spi/ReusableBGPPeer.java [new file with mode: 0644]
bgp/rib-impl/src/test/java/org/opendaylight/protocol/bgp/rib/impl/FSMTest.java
bgp/rib-impl/src/test/java/org/opendaylight/protocol/bgp/rib/impl/SimpleSessionListener.java
bgp/rib-impl/src/test/java/org/opendaylight/protocol/bgp/rib/impl/StrictBGPPeerRegistryTest.java [new file with mode: 0644]
bgp/testtool/src/main/java/org/opendaylight/protocol/bgp/testtool/Main.java
bgp/testtool/src/main/java/org/opendaylight/protocol/bgp/testtool/TestingListener.java
bgp/testtool/src/test/java/org/opendaylight/protocol/bgp/testtool/BGPSpeakerMock.java
integration-tests/src/test/java/org/opendaylight/protocol/integration/bgp/ParserToSalTest.java

index 5e8962667707530e7e934087a1f899ce460f0f5a..fc9a2454891451104fa674688701e04320c489f6 100644 (file)
                     <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:bgp:rib:impl">prefix:base-bgp-parser</type>
                     <name>base-bgp-parser</name>
                 </module>
+                <!-- Global bgp peer registry that should contain all configured bgp peers -->
+                <module>
+                    <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:bgp:rib:impl">prefix:strict-bgp-peer-registry</type>
+                    <name>global-bgp-peer-registry</name>
+                </module>
                 <module>
                     <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:bgp:rib:spi">prefix:bgp-rib-extensions-impl</type>
                     <name>global-rib-extensions</name>
             </modules>
 
             <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+                <service>
+                    <type xmlns:bgpspi="urn:opendaylight:params:xml:ns:yang:controller:bgp:rib:impl">bgpspi:bgp-peer-registry</type>
+                    <instance>
+                        <name>global-bgp-peer-registry</name>
+                        <provider>/modules/module[type='strict-bgp-peer-registry'][name='global-bgp-peer-registry']</provider>
+                    </instance>
+                </service>
                 <service>
                     <type xmlns:bgpspi="urn:opendaylight:params:xml:ns:yang:controller:bgp:parser:spi">bgpspi:extensions</type>
                     <instance>
index 5d716cedc5b08d7116e20c584bf52165bdf6cd71..bdce9c4c3385fb398ff6d3a2636b8aec265ea0a7 100644 (file)
                     </executor>
                 </module>
 
+                <module>
+                    <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:bgp:rib:impl">prefix:bgp-peer-acceptor</type>
+                    <name>bgp-peer-server</name>
+
+                    <!--Default parameters-->
+                    <!--<binding-address>0.0.0.0</binding-address>-->
+                    <!--<binding-port>179</binding-port>-->
+
+                    <bgp-dispatcher>
+                        <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:bgp:rib:impl">prefix:bgp-dispatcher</type>
+                        <name>global-bgp-dispatcher</name>
+                    </bgp-dispatcher>
+                    <peer-registry>
+                        <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:bgp:rib:impl">prefix:bgp-peer-registry</type>
+                        <name>global-bgp-peer-registry</name>
+                    </peer-registry>
+
+                </module>
+
                 <!--
                      A single BGP peer. Note this section is deactivated because a misconfigured peer
                      tends to log rather nasty error messages.
                         <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:bgp:rib:cfg">prefix:rib</type>
                         <name>example-bgp-rib</name>
                     </rib>
+                    <peer-registry>
+                        <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:bgp:rib:impl">prefix:bgp-peer-registry</type>
+                        <name>global-bgp-peer-registry</name>
+                    </peer-registry>
                     <advertized-table>
                         <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:bgp:rib:impl">prefix:bgp-table-type</type>
                         <name>ipv4-unicast</name>
index 1ccb2c4c24c010c30a628b2194a6623c1f54560e..04995dfb57995f1e2213cd4a8432c0d22b4d7026 100644 (file)
@@ -7,6 +7,7 @@
  */
 package org.opendaylight.protocol.bgp.parser;
 
+import com.google.common.base.Objects;
 import org.opendaylight.protocol.framework.TerminationReason;
 
 public final class BGPTerminationReason implements TerminationReason {
@@ -20,4 +21,11 @@ public final class BGPTerminationReason implements TerminationReason {
     public String getErrorMessage() {
         return error.toString();
     }
+
+    @Override
+    public String toString() {
+        return Objects.toStringHelper(this)
+                .add("error", error)
+                .toString();
+    }
 }
diff --git a/bgp/rib-impl-config/src/main/java/org/opendaylight/controller/config/yang/bgp/rib/impl/BGPPeerAcceptorModule.java b/bgp/rib-impl-config/src/main/java/org/opendaylight/controller/config/yang/bgp/rib/impl/BGPPeerAcceptorModule.java
new file mode 100644 (file)
index 0000000..327f93d
--- /dev/null
@@ -0,0 +1,73 @@
+package org.opendaylight.controller.config.yang.bgp.rib.impl;
+
+import com.google.common.collect.Lists;
+import io.netty.channel.ChannelFuture;
+import io.netty.util.concurrent.Future;
+import io.netty.util.concurrent.GenericFutureListener;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.UnknownHostException;
+import org.opendaylight.controller.config.api.JmxAttributeValidationException;
+import org.opendaylight.protocol.bgp.rib.impl.server.BGPServerSessionValidator;
+
+/**
+* BGP peer acceptor that handles incomming bgp connections.
+*/
+public class BGPPeerAcceptorModule extends org.opendaylight.controller.config.yang.bgp.rib.impl.AbstractBGPPeerAcceptorModule {
+    public BGPPeerAcceptorModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+        super(identifier, dependencyResolver);
+    }
+
+    public BGPPeerAcceptorModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.controller.config.yang.bgp.rib.impl.BGPPeerAcceptorModule oldModule, java.lang.AutoCloseable oldInstance) {
+        super(identifier, dependencyResolver, oldModule, oldInstance);
+    }
+
+    @Override
+    public void customValidation() {
+        // Try to parse address
+        try {
+            getAddress();
+        } catch (final IllegalArgumentException e) {
+            throw new JmxAttributeValidationException("Unable to resolve configured address", e, Lists.newArrayList(bindingAddressJmxAttribute, bindingPortJmxAttribute));
+        }
+    }
+
+    @Override
+    public java.lang.AutoCloseable createInstance() {
+        final ChannelFuture future = getBgpDispatcherDependency().createServer(getPeerRegistryDependency(), getAddress(), new BGPServerSessionValidator());
+
+        // Validate future success
+        future.addListener(new GenericFutureListener<Future<Void>>() {
+            @Override
+            public void operationComplete(Future<Void> future) throws Exception {
+                if(future.isSuccess() == false) {
+                    throw new IllegalStateException(String.format("Unable to start bgp server on %s", getAddress()), future.cause());
+                }
+            }
+        });
+
+        return new AutoCloseable() {
+            @Override
+            public void close() throws Exception {
+                // This closes the acceptor and no new bgp connections will be accepted
+                // Connections already established will be preserved
+                future.cancel(true);
+                future.channel().close();
+            }
+        };
+    }
+
+    private InetSocketAddress getAddress() {
+        final InetAddress inetAddr;
+        try {
+            inetAddr = InetAddress.getByName(getBindingAddress()
+                    .getIpv4Address() != null ? getBindingAddress()
+                    .getIpv4Address().getValue() : getBindingAddress()
+                    .getIpv6Address().getValue());
+        } catch (final UnknownHostException e) {
+            throw new IllegalArgumentException("Illegal binding address " + getBindingAddress(), e);
+        }
+        return new InetSocketAddress(inetAddr, getBindingPort().getValue());
+    }
+
+}
diff --git a/bgp/rib-impl-config/src/main/java/org/opendaylight/controller/config/yang/bgp/rib/impl/BGPPeerAcceptorModuleFactory.java b/bgp/rib-impl-config/src/main/java/org/opendaylight/controller/config/yang/bgp/rib/impl/BGPPeerAcceptorModuleFactory.java
new file mode 100644 (file)
index 0000000..cd9bff1
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+* Generated file
+*
+* Generated from: yang module name: bgp-rib-impl yang module local name: bgp-peer-acceptor
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Thu Jul 03 15:13:59 CEST 2014
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.controller.config.yang.bgp.rib.impl;
+public class BGPPeerAcceptorModuleFactory extends org.opendaylight.controller.config.yang.bgp.rib.impl.AbstractBGPPeerAcceptorModuleFactory {
+
+}
index 1cc76ba04a196d45774a493fd50a920b0c75a585..c54c485468c5945ed034826c254bff9df945df60 100644 (file)
  */
 package org.opendaylight.controller.config.yang.bgp.rib.impl;
 
+import com.google.common.base.Charsets;
 import com.google.common.collect.Lists;
 import com.google.common.net.InetAddresses;
+import io.netty.util.concurrent.Future;
+
 import java.net.InetSocketAddress;
 import java.util.List;
+
+import org.opendaylight.bgpcep.tcpmd5.KeyMapping;
 import org.opendaylight.controller.config.api.JmxAttributeValidationException;
 import org.opendaylight.protocol.bgp.rib.impl.BGPPeer;
+import org.opendaylight.protocol.bgp.rib.impl.StrictBGPPeerRegistry;
+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.RIB;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.AsNumber;
@@ -91,21 +98,64 @@ public final class BGPPeerModule extends org.opendaylight.controller.config.yang
         }
     }
 
-    private static String peerName(final IpAddress host) {
-        if (host.getIpv4Address() != null) {
-            return host.getIpv4Address().getValue();
-        }
-        if (host.getIpv6Address() != null) {
-            return host.getIpv6Address().getValue();
+    @Override
+    public java.lang.AutoCloseable createInstance() {
+        final RIB r = getRibDependency();
+
+        final List<BgpParameters> tlvs = getTlvs(r);
+        final AsNumber remoteAs = getAsOrDefault(r);
+        final String password = getPasswordOrNull();
+
+        final BGPSessionPreferences prefs = new BGPSessionPreferences(r.getLocalAs(), getHoldtimer(), r.getBgpIdentifier(), tlvs);
+        final BGPPeer bgpClientPeer = new BGPPeer(peerName(getHostWithoutValue()), r);
+
+        getPeerRegistryBackwards().addPeer(getHostWithoutValue(), bgpClientPeer, prefs);
+
+        final AutoCloseable peerCloseable = new AutoCloseable() {
+            @Override
+            public void close() throws Exception {
+                bgpClientPeer.close();
+                getPeerRegistryBackwards().removePeer(getHostWithoutValue());
+            }
+        };
+
+        // Initiate connection
+        if(getInitiateConnection()) {
+            final Future<Void> cf = initiateConnection(createAddress(), password, remoteAs, getPeerRegistryBackwards());
+            return new AutoCloseable() {
+                @Override
+                public void close() throws Exception {
+                    cf.cancel(true);
+                    peerCloseable.close();
+                }
+            };
+        } else {
+            return peerCloseable;
         }
+    }
 
-        return null;
+    private String getPasswordOrNull() {
+        final String password;
+        if (getPassword() != null) {
+            password = getPassword().getValue();
+        } else {
+            password = null;
+        }
+        return password;
     }
 
-    @Override
-    public java.lang.AutoCloseable createInstance() {
-        final RIB r = getRibDependency();
+    private AsNumber getAsOrDefault(RIB r) {
+        // Remote AS number defaults to our local AS
+        final AsNumber remoteAs;
+        if (getRemoteAs() != null) {
+            remoteAs = new AsNumber(getRemoteAs());
+        } else {
+            remoteAs = r.getLocalAs();
+        }
+        return remoteAs;
+    }
 
+    private List<BgpParameters> getTlvs(RIB r) {
         final List<BgpParameters> tlvs = Lists.newArrayList();
         tlvs.add(new BgpParametersBuilder().setCParameters(
                 new As4BytesCaseBuilder().setAs4BytesCapability(new As4BytesCapabilityBuilder().setAsNumber(r.getLocalAs()).build()).build()).build());
@@ -118,22 +168,49 @@ public final class BGPPeerModule extends org.opendaylight.controller.config.yang
             tlvs.add(new BgpParametersBuilder().setCParameters(
                     new MultiprotocolCaseBuilder().setMultiprotocolCapability(new MultiprotocolCapabilityBuilder(t).build()).build()).build());
         }
+        return tlvs;
+    }
 
-        // Remote AS number defaults to our local AS
-        final AsNumber remoteAs;
-        if (getRemoteAs() != null) {
-            remoteAs = new AsNumber(getRemoteAs());
-        } else {
-            remoteAs = r.getLocalAs();
+    public IpAddress getHostWithoutValue() {
+        // FIXME we need to remove field "value" from IpAddress since equals does not work as expected when value being present
+        // Remove after this bug is fixed https://bugs.opendaylight.org/show_bug.cgi?id=1276
+        final IpAddress host = super.getHost();
+        if(host.getIpv4Address() != null) {
+            return new IpAddress(host.getIpv4Address());
+        } else if(host.getIpv6Address() != null){
+            return new IpAddress(host.getIpv6Address());
         }
 
-        final String password;
-        if (getPassword() != null) {
-            password = getPassword().getValue();
+        throw new IllegalArgumentException("Unexpected host " + host);
+    }
+
+    private io.netty.util.concurrent.Future<Void> initiateConnection(final InetSocketAddress address, final String password, final AsNumber remoteAs, final BGPPeerRegistry registry) {
+        final KeyMapping keys;
+        if (password != null) {
+            keys = new KeyMapping();
+            keys.put(address.getAddress(), password.getBytes(Charsets.US_ASCII));
         } else {
-            password = null;
+            keys = null;
         }
 
-        return new BGPPeer(peerName(getHost()), createAddress(), password, new BGPSessionPreferences(r.getLocalAs(), getHoldtimer(), r.getBgpIdentifier(), tlvs), remoteAs, r);
+        final RIB rib = getRibDependency();
+        return rib.getDispatcher().createReconnectingClient(address, remoteAs, registry, rib.getTcpStrategyFactory(),
+                rib.getSessionStrategyFactory(), keys);
+    }
+
+    private BGPPeerRegistry getPeerRegistryBackwards() {
+        return getPeerRegistry() == null ? StrictBGPPeerRegistry.GLOBAL : getPeerRegistryDependency();
     }
+
+    private static String peerName(final IpAddress host) {
+        if (host.getIpv4Address() != null) {
+            return host.getIpv4Address().getValue();
+        }
+        if (host.getIpv6Address() != null) {
+            return host.getIpv6Address().getValue();
+        }
+
+        return null;
+    }
+
 }
diff --git a/bgp/rib-impl-config/src/main/java/org/opendaylight/controller/config/yang/bgp/rib/impl/StrictBgpPeerRegistryModule.java b/bgp/rib-impl-config/src/main/java/org/opendaylight/controller/config/yang/bgp/rib/impl/StrictBgpPeerRegistryModule.java
new file mode 100644 (file)
index 0000000..8b82279
--- /dev/null
@@ -0,0 +1,84 @@
+package org.opendaylight.controller.config.yang.bgp.rib.impl;
+
+import com.google.common.base.Objects;
+import org.opendaylight.protocol.bgp.parser.BGPDocumentedException;
+import org.opendaylight.protocol.bgp.parser.BGPSessionListener;
+import org.opendaylight.protocol.bgp.rib.impl.StrictBGPPeerRegistry;
+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.ReusableBGPPeer;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
+
+/**
+* Registry of BGP peers that allows only one connection per 2 peers
+*/
+public class StrictBgpPeerRegistryModule extends org.opendaylight.controller.config.yang.bgp.rib.impl.AbstractStrictBgpPeerRegistryModule {
+    public StrictBgpPeerRegistryModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+        super(identifier, dependencyResolver);
+    }
+
+    public StrictBgpPeerRegistryModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, final org.opendaylight.controller.config.yang.bgp.rib.impl.StrictBgpPeerRegistryModule oldModule, final java.lang.AutoCloseable oldInstance) {
+        super(identifier, dependencyResolver, oldModule, oldInstance);
+    }
+
+    @Override
+    public void customValidation() {
+        // add custom validation form module attributes here.
+    }
+
+    @Override
+    public java.lang.AutoCloseable createInstance() {
+        return new GlobalBGPPeerRegistryWrapper(StrictBGPPeerRegistry.GLOBAL);
+    }
+
+    // TODO backwards compatibility, peer-registry has to be mandatory attribute for peers
+    /**
+     * Wrapper for BGPPeerRegistry that prevents from executing close method
+      */
+    private static final class GlobalBGPPeerRegistryWrapper implements BGPPeerRegistry, AutoCloseable {
+        private final StrictBGPPeerRegistry global;
+
+        public GlobalBGPPeerRegistryWrapper(final StrictBGPPeerRegistry global) {
+            this.global = global;
+        }
+
+        @Override
+        public BGPSessionPreferences getPeerPreferences(final IpAddress ip) {
+            return global.getPeerPreferences(ip);
+        }
+
+        @Override
+        public BGPSessionListener getPeer(final IpAddress ip, final Ipv4Address sourceId, final Ipv4Address remoteId) throws BGPDocumentedException {
+            return global.getPeer(ip, sourceId, remoteId);
+        }
+
+        @Override
+        public boolean isPeerConfigured(final IpAddress ip) {
+            return global.isPeerConfigured(ip);
+        }
+
+        @Override
+        public void removePeer(final IpAddress ip) {
+            global.removePeer(ip);
+        }
+
+        @Override
+        public void addPeer(final IpAddress ip, final ReusableBGPPeer peer, final BGPSessionPreferences preferences) {
+            global.addPeer(ip, peer, preferences);
+        }
+
+        @Override
+        public void close() throws Exception {
+            // DO nothing, do not close the global instance
+        }
+
+        @Override
+        public String toString() {
+            return Objects.toStringHelper(this)
+                    .add("peers", global)
+                    .toString();
+        }
+    }
+
+}
diff --git a/bgp/rib-impl-config/src/main/java/org/opendaylight/controller/config/yang/bgp/rib/impl/StrictBgpPeerRegistryModuleFactory.java b/bgp/rib-impl-config/src/main/java/org/opendaylight/controller/config/yang/bgp/rib/impl/StrictBgpPeerRegistryModuleFactory.java
new file mode 100644 (file)
index 0000000..2009cd3
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+* Generated file
+*
+* Generated from: yang module name: bgp-rib-impl yang module local name: strict-bgp-peer-registry
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Thu Jul 03 16:40:54 CEST 2014
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.controller.config.yang.bgp.rib.impl;
+
+public class StrictBgpPeerRegistryModuleFactory extends org.opendaylight.controller.config.yang.bgp.rib.impl.AbstractStrictBgpPeerRegistryModuleFactory {
+
+}
index b0df01aae22db95c7a04fe25fceb6a32e3fd0ea9..05fea88a163fcb8677bf376ef51164484eb8eadd 100644 (file)
@@ -144,6 +144,30 @@ module bgp-rib-impl {
         config:java-class "org.opendaylight.protocol.bgp.rib.impl.spi.RIB";
     }
 
+    identity bgp-peer-registry {
+        description
+            "Registry of BGP peers. Every new BGP in/out connection looks for peers to handle bgp messages in this registry";
+
+        base "config:service-type";
+        config:java-class "org.opendaylight.protocol.bgp.rib.impl.spi.BGPPeerRegistry";
+    }
+
+    identity strict-bgp-peer-registry {
+        description
+            "Registry of BGP peers that allows only one connection per 2 peers. Uses IP address for Peer identification and BGP Ids to resolve duplicate connections";
+
+        config:provided-service bgp-peer-registry;
+        base config:module-type;
+        config:java-name-prefix StrictBgpPeerRegistry;
+    }
+
+
+     augment "/config:modules/config:module/config:configuration" {
+        case strict-bgp-peer-registry {
+            when "/config:modules/config:module/config:type = 'strict-bgp-peer-registry'";
+        }
+    }
+
     identity bgp-peer {
         description
             "BGP peer instance.";
@@ -152,6 +176,50 @@ module bgp-rib-impl {
         config:java-name-prefix BGPPeer;
     }
 
+    identity bgp-peer-acceptor {
+        description
+            "BGP peer acceptor that handles incomming bgp connections. Uses BGP peer registry to accept or decline incomming connections";
+
+        base config:module-type;
+        config:java-name-prefix BGPPeerAcceptor;
+    }
+
+     augment "/config:modules/config:module/config:configuration" {
+        case bgp-peer-acceptor {
+            when "/config:modules/config:module/config:type = 'bgp-peer-acceptor'";
+
+            leaf binding-address {
+                description "IP address to bind to";
+                type inet:ip-address;
+                default "0.0.0.0";
+            }
+
+            leaf binding-port {
+                description "Port to bind to";
+                type inet:port-number;
+                default "179";
+            }
+
+            container bgp-dispatcher {
+                uses config:service-ref {
+                    refine type {
+                        mandatory true;
+                        config:required-identity bgp-dispatcher;
+                    }
+                }
+            }
+
+            container peer-registry {
+                uses config:service-ref {
+                    refine type {
+                        mandatory true;
+                        config:required-identity bgp-peer-registry;
+                    }
+                }
+            }
+        }
+    }
+
     identity bgp-table-type {
         description
             "Service representing a AFI/SAFI pair";
@@ -209,6 +277,13 @@ module bgp-rib-impl {
                 default 180;
             }
 
+            leaf initiate-connection {
+                description "If true, connection will be initiated right away from current device.
+                    If not, the peer will only be registered to peer registry and available for incomming bgp connections.";
+                type boolean;
+                default true;
+            }
+
             list advertized-table {
                 uses config:service-ref {
                     refine type {
@@ -238,6 +313,17 @@ module bgp-rib-impl {
                     }
                 }
             }
+
+            container peer-registry {
+                description "BGP peer registry where current instance of BGP peer will be registered.";
+                uses config:service-ref {
+                    refine type {
+                        // FIXME backwards compatibility. If not configured, GLOBAL instance is used
+                        mandatory false;
+                        config:required-identity bgp-peer-registry;
+                    }
+                }
+            }
         }
     }
 
diff --git a/bgp/rib-impl-config/src/test/java/org/opendaylight/controller/config/yang/bgp/rib/impl/BGPPeerAcceptorModuleTest.java b/bgp/rib-impl-config/src/test/java/org/opendaylight/controller/config/yang/bgp/rib/impl/BGPPeerAcceptorModuleTest.java
new file mode 100644 (file)
index 0000000..fb5f3f6
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.config.yang.bgp.rib.impl;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.Lists;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import io.netty.util.concurrent.GenericFutureListener;
+import java.net.InetSocketAddress;
+import java.util.List;
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.ObjectName;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.config.api.ConflictingVersionException;
+import org.opendaylight.controller.config.api.DynamicMBeanWithInstance;
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.config.api.ValidationException;
+import org.opendaylight.controller.config.api.jmx.CommitStatus;
+import org.opendaylight.controller.config.manager.impl.AbstractConfigTest;
+import org.opendaylight.controller.config.manager.impl.AbstractMockedModule;
+import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver;
+import org.opendaylight.controller.config.spi.ModuleFactory;
+import org.opendaylight.controller.config.util.ConfigTransactionJMXClient;
+import org.opendaylight.controller.config.yang.bgp.parser.spi.SimpleBGPExtensionProviderContextModuleFactory;
+import org.opendaylight.controller.config.yang.netty.threadgroup.NettyThreadgroupModuleFactory;
+import org.opendaylight.controller.config.yang.netty.timer.HashedWheelTimerModuleFactory;
+import org.opendaylight.protocol.bgp.rib.impl.spi.BGPDispatcher;
+import org.opendaylight.protocol.bgp.rib.impl.spi.BGPPeerRegistry;
+import org.opendaylight.protocol.bgp.rib.impl.spi.BGPSessionValidator;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.PortNumber;
+
+public class BGPPeerAcceptorModuleTest extends AbstractConfigTest {
+
+    private static final String INSTANCE_NAME = "bgp-peer-acceptor";
+    private static final String FACTORY_NAME = BGPPeerAcceptorModuleFactory.NAME;
+
+    @Before
+    public void setUp() throws Exception {
+        final List<ModuleFactory> moduleFactories = getModuleFactories();
+        super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver(mockedContext, moduleFactories.toArray(new ModuleFactory[moduleFactories.size()])));
+    }
+
+    private List<ModuleFactory> getModuleFactories() {
+        final List<ModuleFactory> moduleFactories = Lists.newArrayList();
+        moduleFactories.add(new StrictBgpPeerRegistryModuleFactory());
+        moduleFactories.add(new BGPPeerAcceptorModuleFactory());
+        moduleFactories.add(new NettyThreadgroupModuleFactory());
+        moduleFactories.add(new SimpleBGPExtensionProviderContextModuleFactory());
+        moduleFactories.add(new HashedWheelTimerModuleFactory());
+        moduleFactories.add(createClassBasedCBF(MockedDispatcherModule.class, "dispatch"));
+        return moduleFactories;
+    }
+
+    @Test
+    public void testCreateBeanDefaultAddress() throws Exception {
+        final CommitStatus status = createRegistryInstance(Optional.<String>absent(), Optional.<Integer>absent(), true, true);
+        assertBeanCount(1, FACTORY_NAME);
+        assertStatus(status, 3, 0, 0);
+        verify(dispatcher).createServer(any(BGPPeerRegistry.class), any(InetSocketAddress.class), any(BGPSessionValidator.class));
+    }
+
+    @Test
+    public void testCreateBean() throws Exception {
+        final CommitStatus status = createRegistryInstance(Optional.of("127.0.0.1"), Optional.of(1790), true, true);
+        assertBeanCount(1, FACTORY_NAME);
+        assertStatus(status, 3, 0, 0);
+        verify(dispatcher).createServer(any(BGPPeerRegistry.class), any(InetSocketAddress.class), any(BGPSessionValidator.class));
+    }
+
+    private CommitStatus createRegistryInstance(final Optional<String> address, final Optional<Integer> port, final boolean addRegistry, final boolean addDispatcher ) throws InstanceAlreadyExistsException, ValidationException, ConflictingVersionException {
+        final ConfigTransactionJMXClient transaction = this.configRegistryClient.createTransaction();
+        final ObjectName module = transaction.createModule(FACTORY_NAME, INSTANCE_NAME);
+        final BGPPeerAcceptorModuleMXBean proxy = transaction.newMXBeanProxy(module, BGPPeerAcceptorModuleMXBean.class);
+
+        // FIXME JMX crashes if union was not created via artificial constructor - Bug:1276
+        if(address.isPresent()) {
+            proxy.setBindingAddress(new IpAddress(address.get().toCharArray()));
+        }
+        if(port.isPresent()) {
+            proxy.setBindingPort(new PortNumber(port.get()));
+        }
+        if(addRegistry) {
+            proxy.setPeerRegistry(createPeerRegistry(transaction));
+        }
+        if(addDispatcher) {
+            proxy.setBgpDispatcher(createDispatcher(transaction));
+        }
+        return transaction.commit();
+    }
+
+    private ObjectName createPeerRegistry(final ConfigTransactionJMXClient transaction) throws InstanceAlreadyExistsException {
+        return transaction.createModule(StrictBgpPeerRegistryModuleFactory.NAME, "peer-registry");
+    }
+
+    private ObjectName createDispatcher(final ConfigTransactionJMXClient transaction) throws InstanceAlreadyExistsException {
+        return transaction.createModule("dispatch", "mock");
+    }
+
+    private static interface MockDispatcher extends BGPDispatcher, AutoCloseable {}
+
+    @Mock
+    static MockDispatcher dispatcher;
+
+    @Before
+    public void setUpMockDispatcher() throws Exception {
+        MockitoAnnotations.initMocks(BGPPeerAcceptorModuleTest.this);
+        final ChannelFuture future = mock(ChannelFuture.class);
+        doReturn(true).when(future).cancel(anyBoolean());
+        final Channel channel = mock(Channel.class);
+        doReturn(mock(ChannelFuture.class)).when(channel).close();
+        doReturn(channel).when(future).channel();
+        doReturn(mock(ChannelFuture.class)).when(future).addListener(any(GenericFutureListener.class));
+        doReturn(future).when(dispatcher).createServer(any(BGPPeerRegistry.class), any(InetSocketAddress.class), any(BGPSessionValidator.class));
+        doNothing().when(dispatcher).close();
+    }
+
+    public final static class MockedDispatcherModule extends AbstractMockedModule implements BGPDispatcherImplModuleMXBean, BGPDispatcherServiceInterface {
+
+        public MockedDispatcherModule(final DynamicMBeanWithInstance old, final ModuleIdentifier id) {
+            super(old, id);
+        }
+
+        @Override
+        protected AutoCloseable prepareMockedInstance() throws Exception {return dispatcher;}
+
+        @Override
+        public ObjectName getWorkerGroup() {return null;}
+
+        @Override
+        public void setWorkerGroup(final ObjectName workerGroup) {}
+
+        @Override
+        public ObjectName getTimer() {return null;}
+
+        @Override
+        public void setTimer(final ObjectName timer) {}
+
+        @Override
+        public ObjectName getBgpExtensions() {return null;}
+
+        @Override
+        public void setBgpExtensions(final ObjectName bgpExtensions) {}
+
+        @Override
+        public ObjectName getMd5ChannelFactory() {return null;}
+
+        @Override
+        public void setMd5ChannelFactory(final ObjectName md5ChannelFactory) {}
+
+        @Override
+        public ObjectName getBossGroup() {return null;}
+
+        @Override
+        public void setBossGroup(final ObjectName bossGroup) {}
+
+        @Override
+        public ObjectName getMd5ServerChannelFactory() {return null;}
+
+        @Override
+        public void setMd5ServerChannelFactory(final ObjectName md5ServerChannelFactory) {}
+    }
+}
index aed81cbcd258d3855db9dd09e3db1bc5825f283e..3fa6b0057cc7705051911e3251d89816f8da30d4 100644 (file)
@@ -15,6 +15,7 @@ import static org.mockito.Mockito.mock;
 import com.google.common.collect.Lists;
 import java.util.Collections;
 import java.util.List;
+import javax.management.InstanceAlreadyExistsException;
 import javax.management.ObjectName;
 import org.junit.Test;
 import org.opendaylight.bgpcep.tcpmd5.jni.NativeTestSupport;
@@ -60,6 +61,7 @@ public class BGPPeerModuleTest extends AbstractRIBImplModuleTest {
         moduleFactories.add(new BGPTableTypeImplModuleFactory());
         moduleFactories.add(new NativeKeyAccessFactoryModuleFactory());
         moduleFactories.add(new MD5ClientChannelFactoryModuleFactory());
+        moduleFactories.add(new StrictBgpPeerRegistryModuleFactory());
         return moduleFactories;
     }
 
@@ -87,7 +89,7 @@ public class BGPPeerModuleTest extends AbstractRIBImplModuleTest {
     public void testCreateBean() throws Exception {
         final CommitStatus status = createBgpPeerInstance();
         assertBeanCount(1, FACTORY_NAME);
-        assertStatus(status, 16, 0, 0);
+        assertStatus(status, 17, 0, 0);
     }
 
     @Test
@@ -95,7 +97,7 @@ public class BGPPeerModuleTest extends AbstractRIBImplModuleTest {
         NativeTestSupport.assumeSupportedPlatform();
         final CommitStatus status = createBgpPeerInstance(true);
         assertBeanCount(1, FACTORY_NAME);
-        assertStatus(status, 18, 0, 0);
+        assertStatus(status, 19, 0, 0);
     }
 
     @Test
@@ -123,7 +125,7 @@ public class BGPPeerModuleTest extends AbstractRIBImplModuleTest {
         assertBeanCount(1, FACTORY_NAME);
         status = transaction.commit();
         assertBeanCount(1, FACTORY_NAME);
-        assertStatus(status, 0, 0, 16);
+        assertStatus(status, 0, 0, 17);
     }
 
     @Test
@@ -136,7 +138,7 @@ public class BGPPeerModuleTest extends AbstractRIBImplModuleTest {
         mxBean.setPort(new PortNumber(10));
         status = transaction.commit();
         assertBeanCount(1, FACTORY_NAME);
-        assertStatus(status, 0, 1, 15);
+        assertStatus(status, 0, 1, 16);
     }
 
     private ObjectName createBgpPeerInstance(final ConfigTransactionJMXClient transaction, final String host,
@@ -145,6 +147,8 @@ public class BGPPeerModuleTest extends AbstractRIBImplModuleTest {
         final ObjectName nameCreated = transaction.createModule(FACTORY_NAME, INSTANCE_NAME);
         final BGPPeerModuleMXBean mxBean = transaction.newMXBeanProxy(nameCreated, BGPPeerModuleMXBean.class);
 
+        mxBean.setPeerRegistry(createPeerRegistry(transaction));
+
         // FIXME JMX crashes if union was not created via artificial constructor - Bug:1276
         // annotated for JMX as value
         // IpAddress host1 = new IpAddress(new Ipv4Address(host));
@@ -176,6 +180,10 @@ public class BGPPeerModuleTest extends AbstractRIBImplModuleTest {
         return nameCreated;
     }
 
+    private ObjectName createPeerRegistry(final ConfigTransactionJMXClient transaction) throws InstanceAlreadyExistsException {
+        return transaction.createModule(StrictBgpPeerRegistryModuleFactory.NAME, "peer-registry");
+    }
+
     private BGPDispatcherImplModuleMXBean getBgpDispatcherImplModuleMXBean(ConfigTransactionJMXClient transaction,
                                                                            BGPPeerModuleMXBean mxBean) {
         RIBImplModuleMXBean ribProxy = transaction.newMXBeanProxy(mxBean.getRib(), RIBImplModuleMXBean.class);
similarity index 60%
rename from bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/BGPSessionNegotiator.java
rename to bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AbstractBGPSessionNegotiator.java
index d8e445e18f51dc10379faf213bb1985b8c794521..3eebcb1fcfaac1a81d946dac1ee3f2078ee445ed 100644 (file)
@@ -1,46 +1,47 @@
 /*
- * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
+
 package org.opendaylight.protocol.bgp.rib.impl;
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
-
 import io.netty.channel.Channel;
 import io.netty.util.Timeout;
 import io.netty.util.Timer;
 import io.netty.util.TimerTask;
 import io.netty.util.concurrent.Promise;
-
-import java.util.List;
 import java.util.concurrent.TimeUnit;
-
 import javax.annotation.concurrent.GuardedBy;
-
-import org.opendaylight.protocol.bgp.parser.AsNumberUtil;
 import org.opendaylight.protocol.bgp.parser.BGPDocumentedException;
 import org.opendaylight.protocol.bgp.parser.BGPError;
 import org.opendaylight.protocol.bgp.parser.BGPSessionListener;
+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.BGPSessionValidator;
 import org.opendaylight.protocol.framework.AbstractSessionNegotiator;
 import org.opendaylight.protocol.util.Values;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.AsNumber;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.Keepalive;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.KeepaliveBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.Notify;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.NotifyBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.Open;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.OpenBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.open.BgpParameters;
 import org.opendaylight.yangtools.yang.binding.Notification;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public final class BGPSessionNegotiator extends AbstractSessionNegotiator<Notification, BGPSessionImpl> {
+/**
+ * Bgp Session negotiator. Common for local -> remote and remote -> local connections.
+ * One difference is session validation performed by injected BGPSessionValidator when OPEN message is received.
+ */
+public abstract class AbstractBGPSessionNegotiator extends AbstractSessionNegotiator<Notification, BGPSessionImpl> {
     // 4 minutes recommended in http://tools.ietf.org/html/rfc4271#section-8.2.2
     protected static final int INITIAL_HOLDTIMER = 4;
 
@@ -70,11 +71,10 @@ public final class BGPSessionNegotiator extends AbstractSessionNegotiator<Notifi
         Finished,
     }
 
-    private static final Logger LOG = LoggerFactory.getLogger(BGPSessionNegotiator.class);
-    private final BGPSessionPreferences localPref;
-    private final BGPSessionListener listener;
-    private final AsNumber remoteAs;
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractBGPSessionNegotiator.class);
     private final Timer timer;
+    private final BGPPeerRegistry registry;
+    private final BGPSessionValidator sessionValidator;
 
     @GuardedBy("this")
     private State state = State.Idle;
@@ -82,25 +82,37 @@ public final class BGPSessionNegotiator extends AbstractSessionNegotiator<Notifi
     @GuardedBy("this")
     private BGPSessionImpl session;
 
-    public BGPSessionNegotiator(final Timer timer, final Promise<BGPSessionImpl> promise, final Channel channel,
-            final BGPSessionPreferences initialPrefs, final AsNumber remoteAs, final BGPSessionListener listener) {
+    public AbstractBGPSessionNegotiator(final Timer timer, final Promise<BGPSessionImpl> promise, final Channel channel,
+                                        final BGPPeerRegistry registry, final BGPSessionValidator sessionValidator) {
         super(promise, channel);
-        this.listener = Preconditions.checkNotNull(listener);
-        this.localPref = Preconditions.checkNotNull(initialPrefs);
-        this.remoteAs = Preconditions.checkNotNull(remoteAs);
+        this.registry = registry;
+        this.sessionValidator = sessionValidator;
         this.timer = Preconditions.checkNotNull(timer);
     }
 
     @Override
     protected void startNegotiation() {
         Preconditions.checkState(this.state == State.Idle);
-        int as = this.localPref.getMyAs().getValue().intValue();
+
+        // Check if peer is configured in registry before retrieving preferences
+        if (registry.isPeerConfigured(getRemoteIp()) == false) {
+            final BGPDocumentedException cause = new BGPDocumentedException(
+                    "BGP peer with ip: " + getRemoteIp()
+                            + " not configured, check configured peers in : "
+                            + registry, BGPError.CEASE);
+            negotiationFailed(cause);
+            return;
+        }
+
+        final BGPSessionPreferences preferences = getPreferences();
+
+        int as = preferences.getMyAs().getValue().intValue();
         // Set as AS_TRANS if the value is bigger than 2B
         if (as > Values.UNSIGNED_SHORT_MAX_VALUE) {
             as = AS_TRANS;
         }
-        this.sendMessage(new OpenBuilder().setMyAsNumber(as).setHoldTimer(this.localPref.getHoldTime()).setBgpIdentifier(
-                this.localPref.getBgpId()).setBgpParameters(this.localPref.getParams()).build());
+        this.sendMessage(new OpenBuilder().setMyAsNumber(as).setHoldTimer(preferences.getHoldTime()).setBgpIdentifier(
+                preferences.getBgpId()).setBgpParameters(preferences.getParams()).build());
         this.state = State.OpenSent;
 
         final Object lock = this;
@@ -108,16 +120,24 @@ public final class BGPSessionNegotiator extends AbstractSessionNegotiator<Notifi
             @Override
             public void run(final Timeout timeout) {
                 synchronized (lock) {
-                    if (BGPSessionNegotiator.this.state != State.Finished) {
-                        BGPSessionNegotiator.this.sendMessage(buildErrorNotify(BGPError.HOLD_TIMER_EXPIRED));
+                    if (AbstractBGPSessionNegotiator.this.state != State.Finished) {
+                        AbstractBGPSessionNegotiator.this.sendMessage(buildErrorNotify(BGPError.HOLD_TIMER_EXPIRED));
                         negotiationFailed(new BGPDocumentedException("HoldTimer expired", BGPError.FSM_ERROR));
-                        BGPSessionNegotiator.this.state = State.Finished;
+                        AbstractBGPSessionNegotiator.this.state = State.Finished;
                     }
                 }
             }
         }, INITIAL_HOLDTIMER, TimeUnit.MINUTES);
     }
 
+    private BGPSessionPreferences getPreferences() {
+        return registry.getPeerPreferences(getRemoteIp());
+    }
+
+    private IpAddress getRemoteIp() {
+        return StrictBGPPeerRegistry.getIpAddress(channel.remoteAddress());
+    }
+
     @Override
     protected synchronized void handleMessage(final Notification msg) {
         LOG.debug("Channel {} handling message in state {}", this.channel, this.state);
@@ -158,32 +178,42 @@ public final class BGPSessionNegotiator extends AbstractSessionNegotiator<Notifi
     }
 
     private void handleOpen(final Open openObj) {
-        final AsNumber as = AsNumberUtil.advertizedAsNumber(openObj);
-        if (!this.remoteAs.equals(as)) {
-            LOG.warn("Unexpected remote AS number. Expecting {}, got {}", this.remoteAs, as);
-            this.sendMessage(buildErrorNotify(BGPError.BAD_PEER_AS));
-            negotiationFailed(new BGPDocumentedException("Peer AS number mismatch", BGPError.BAD_PEER_AS));
-            this.state = State.Finished;
+        try {
+            sessionValidator.validate(openObj, getPreferences());
+        } catch (final BGPDocumentedException e) {
+            negotiationFailed(e);
             return;
         }
 
-        final List<BgpParameters> prefs = openObj.getBgpParameters();
-        if (prefs != null && !prefs.isEmpty()) {
-            if (!prefs.containsAll(this.localPref.getParams())) {
-                LOG.info("BGP Open message session parameters differ, session still accepted.");
-            }
+        try {
+            final BGPSessionListener peer = registry.getPeer(getRemoteIp(), getSourceId(openObj, getPreferences()), getDestinationId(openObj, getPreferences()));
             this.sendMessage(new KeepaliveBuilder().build());
-            this.session = new BGPSessionImpl(this.timer, this.listener, this.channel, openObj, this.localPref.getHoldTime());
+            this.session = new BGPSessionImpl(this.timer, peer, this.channel, openObj, getPreferences().getHoldTime());
             this.state = State.OpenConfirm;
             LOG.debug("Channel {} moved to OpenConfirm state with remote proposal {}", this.channel, openObj);
-            return;
+        } catch (final BGPDocumentedException e) {
+            LOG.warn("Channel {} negotiation failed", this.channel, e);
+            negotiationFailed(e);
         }
+    }
 
-        this.sendMessage(buildErrorNotify(BGPError.UNSPECIFIC_OPEN_ERROR));
-        negotiationFailed(new BGPDocumentedException("Open message unacceptable. Check the configuration of BGP speaker.", BGPError.UNSPECIFIC_OPEN_ERROR));
+    private void negotiationFailed(final BGPDocumentedException e) {
+        LOG.warn("Channel {} negotiation failed: {}", this.channel, e.getMessage());
+        this.sendMessage(buildErrorNotify(e.getError()));
+        super.negotiationFailed(e);
         this.state = State.Finished;
     }
 
+    /**
+     * @return BGP Id of device that accepted the connection
+     */
+    protected abstract Ipv4Address getDestinationId(final Open openMsg, final BGPSessionPreferences preferences);
+
+    /**
+     * @return BGP Id of device that initiated the connection
+     */
+    protected abstract Ipv4Address getSourceId(final Open openMsg, final BGPSessionPreferences preferences);
+
     public synchronized State getState() {
         return this.state;
     }
index 1f0de8918190e59b794680a9bec3cbf72213db52..9065f3cc00fcdff8b4f00c06c7d0078249b622f8 100644 (file)
@@ -8,35 +8,36 @@
 package org.opendaylight.protocol.bgp.rib.impl;
 
 import com.google.common.base.Preconditions;
-
 import io.netty.bootstrap.Bootstrap;
 import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.ChannelFuture;
 import io.netty.channel.EventLoopGroup;
 import io.netty.channel.socket.SocketChannel;
 import io.netty.util.Timer;
 import io.netty.util.concurrent.Future;
 import io.netty.util.concurrent.Promise;
-
 import java.net.InetSocketAddress;
-
 import org.opendaylight.bgpcep.tcpmd5.KeyMapping;
 import org.opendaylight.bgpcep.tcpmd5.netty.MD5ChannelFactory;
 import org.opendaylight.bgpcep.tcpmd5.netty.MD5ChannelOption;
 import org.opendaylight.bgpcep.tcpmd5.netty.MD5ServerChannelFactory;
 import org.opendaylight.protocol.bgp.parser.BGPSessionListener;
 import org.opendaylight.protocol.bgp.parser.spi.MessageRegistry;
+import org.opendaylight.protocol.bgp.rib.impl.client.BGPClientSessionNegotiatorFactory;
+import org.opendaylight.protocol.bgp.rib.impl.server.BGPServerSessionNegotiatorFactory;
 import org.opendaylight.protocol.bgp.rib.impl.spi.BGPDispatcher;
-import org.opendaylight.protocol.bgp.rib.impl.spi.BGPSessionPreferences;
+import org.opendaylight.protocol.bgp.rib.impl.spi.BGPPeerRegistry;
+import org.opendaylight.protocol.bgp.rib.impl.spi.BGPServerDispatcher;
+import org.opendaylight.protocol.bgp.rib.impl.spi.BGPSessionValidator;
 import org.opendaylight.protocol.framework.AbstractDispatcher;
 import org.opendaylight.protocol.framework.ReconnectStrategy;
 import org.opendaylight.protocol.framework.ReconnectStrategyFactory;
-import org.opendaylight.protocol.framework.SessionListenerFactory;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.AsNumber;
 
 /**
  * Implementation of BGPDispatcher.
  */
-public final class BGPDispatcherImpl extends AbstractDispatcher<BGPSessionImpl, BGPSessionListener> implements BGPDispatcher, AutoCloseable {
+public final class BGPDispatcherImpl extends AbstractDispatcher<BGPSessionImpl, BGPSessionListener> implements BGPDispatcher, BGPServerDispatcher, AutoCloseable {
     private final MD5ServerChannelFactory<?> scf;
     private final MD5ChannelFactory<?> cf;
     private final BGPHandlerFactory hf;
@@ -58,30 +59,24 @@ public final class BGPDispatcherImpl extends AbstractDispatcher<BGPSessionImpl,
     }
 
     @Override
-    public synchronized Future<BGPSessionImpl> createClient(final InetSocketAddress address, final BGPSessionPreferences preferences,
-            final AsNumber remoteAs, final BGPSessionListener listener, final ReconnectStrategy strategy) {
-        final BGPSessionNegotiatorFactory snf = new BGPSessionNegotiatorFactory(this.timer, preferences, remoteAs);
-        final SessionListenerFactory<BGPSessionListener> slf = new SessionListenerFactory<BGPSessionListener>() {
-            @Override
-            public BGPSessionListener getSessionListener() {
-                return listener;
-            }
-        };
+    public synchronized Future<BGPSessionImpl> createClient(final InetSocketAddress address,
+            final AsNumber remoteAs, final BGPPeerRegistry listener, final ReconnectStrategy strategy) {
+        final BGPClientSessionNegotiatorFactory snf = new BGPClientSessionNegotiatorFactory(this.timer, remoteAs, listener);
         return super.createClient(address, strategy, new PipelineInitializer<BGPSessionImpl>() {
             @Override
             public void initializeChannel(final SocketChannel ch, final Promise<BGPSessionImpl> promise) {
                 ch.pipeline().addLast(BGPDispatcherImpl.this.hf.getDecoders());
-                ch.pipeline().addLast("negotiator", snf.getSessionNegotiator(slf, ch, promise));
+                ch.pipeline().addLast("negotiator", snf.getSessionNegotiator(null, ch, promise));
                 ch.pipeline().addLast(BGPDispatcherImpl.this.hf.getEncoders());
             }
         });
     }
 
     @Override
-    public Future<Void> createReconnectingClient(final InetSocketAddress address, final BGPSessionPreferences preferences,
-            final AsNumber remoteAs, final BGPSessionListener listener, final ReconnectStrategyFactory connectStrategyFactory,
+    public Future<Void> createReconnectingClient(final InetSocketAddress address,
+            final AsNumber remoteAs, final BGPPeerRegistry listener, final ReconnectStrategyFactory connectStrategyFactory,
             final ReconnectStrategyFactory reestablishStrategyFactory) {
-        return this.createReconnectingClient(address, preferences, remoteAs, listener, connectStrategyFactory, reestablishStrategyFactory,
+        return this.createReconnectingClient(address, remoteAs, listener, connectStrategyFactory, reestablishStrategyFactory,
                 null);
     }
 
@@ -90,16 +85,10 @@ public final class BGPDispatcherImpl extends AbstractDispatcher<BGPSessionImpl,
     }
 
     @Override
-    public synchronized Future<Void> createReconnectingClient(final InetSocketAddress address, final BGPSessionPreferences preferences,
-            final AsNumber remoteAs, final BGPSessionListener listener, final ReconnectStrategyFactory connectStrategyFactory,
+    public synchronized Future<Void> createReconnectingClient(final InetSocketAddress address,
+            final AsNumber remoteAs, final BGPPeerRegistry peerRegistry, final ReconnectStrategyFactory connectStrategyFactory,
             final ReconnectStrategyFactory reestablishStrategyFactory, final KeyMapping keys) {
-        final BGPSessionNegotiatorFactory snf = new BGPSessionNegotiatorFactory(this.timer, preferences, remoteAs);
-        final SessionListenerFactory<BGPSessionListener> slf = new SessionListenerFactory<BGPSessionListener>() {
-            @Override
-            public BGPSessionListener getSessionListener() {
-                return listener;
-            }
-        };
+        final BGPClientSessionNegotiatorFactory snf = new BGPClientSessionNegotiatorFactory(this.timer, remoteAs, peerRegistry);
 
         this.keys = keys;
         final Future<Void> ret = super.createReconnectingClient(address, connectStrategyFactory,
@@ -107,7 +96,7 @@ public final class BGPDispatcherImpl extends AbstractDispatcher<BGPSessionImpl,
                     @Override
                     public void initializeChannel(final SocketChannel ch, final Promise<BGPSessionImpl> promise) {
                         ch.pipeline().addLast(BGPDispatcherImpl.this.hf.getDecoders());
-                        ch.pipeline().addLast("negotiator", snf.getSessionNegotiator(slf, ch, promise));
+                        ch.pipeline().addLast("negotiator", snf.getSessionNegotiator(null, ch, promise));
                         ch.pipeline().addLast(BGPDispatcherImpl.this.hf.getEncoders());
                     }
                 });
@@ -116,6 +105,29 @@ public final class BGPDispatcherImpl extends AbstractDispatcher<BGPSessionImpl,
         return ret;
     }
 
+    @Override
+    public ChannelFuture createServer(final BGPPeerRegistry registry, final InetSocketAddress address, final BGPSessionValidator sessionValidator) {
+        return this.createServer(registry, address, sessionValidator, null);
+    }
+
+    @Override
+    public ChannelFuture createServer(final BGPPeerRegistry registry, final InetSocketAddress address, final BGPSessionValidator sessionValidator, final KeyMapping keys) {
+        final BGPServerSessionNegotiatorFactory snf = new BGPServerSessionNegotiatorFactory(this.timer, sessionValidator, registry);
+
+        this.keys = keys;
+        final ChannelFuture ret = super.createServer(address, new PipelineInitializer<BGPSessionImpl>() {
+            @Override
+            public void initializeChannel(final SocketChannel ch, final Promise<BGPSessionImpl> promise) {
+                ch.pipeline().addLast(BGPDispatcherImpl.this.hf.getDecoders());
+                ch.pipeline().addLast("negotiator", snf.getSessionNegotiator(null, ch, promise));
+                ch.pipeline().addLast(BGPDispatcherImpl.this.hf.getEncoders());
+            }
+        });
+        this.keys = null;
+
+        return ret;
+    }
+
     @Override
     protected void customizeBootstrap(final Bootstrap b) {
         if (keys != null && !keys.isEmpty()) {
index 0e9ac2c23634c1ce0c427cb4eef73aa62c01de13..05f03738bcbdfd54344e34342363ced143073ac3 100644 (file)
@@ -1,34 +1,25 @@
 /*
- * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
+
 package org.opendaylight.protocol.bgp.rib.impl;
 
-import com.google.common.base.Charsets;
 import com.google.common.base.Objects;
 import com.google.common.base.Objects.ToStringHelper;
 import com.google.common.base.Preconditions;
-
-import io.netty.util.concurrent.Future;
-
-import java.net.InetSocketAddress;
 import java.util.Comparator;
 import java.util.HashSet;
 import java.util.Set;
-
 import javax.annotation.concurrent.GuardedBy;
-
-import org.opendaylight.bgpcep.tcpmd5.KeyMapping;
 import org.opendaylight.protocol.bgp.parser.BGPSession;
-import org.opendaylight.protocol.bgp.parser.BGPSessionListener;
 import org.opendaylight.protocol.bgp.parser.BGPTerminationReason;
-import org.opendaylight.protocol.bgp.rib.impl.spi.BGPSessionPreferences;
 import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
+import org.opendaylight.protocol.bgp.rib.impl.spi.ReusableBGPPeer;
 import org.opendaylight.protocol.bgp.rib.spi.Peer;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.AsNumber;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.PathAttributes;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.Update;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.BgpTableType;
@@ -37,49 +28,31 @@ import org.opendaylight.yangtools.yang.binding.Notification;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+
 /**
  * Class representing a peer. We have a single instance for each peer, which provides translation from BGP events into
  * RIB actions.
  */
-public final class BGPPeer implements BGPSessionListener, Peer, AutoCloseable {
+public class BGPPeer implements ReusableBGPPeer, Peer, AutoCloseable {
     private static final Logger LOG = LoggerFactory.getLogger(BGPPeer.class);
 
     @GuardedBy("this")
     private final Set<TablesKey> tables = new HashSet<>();
-    private final String name;
     private final RIB rib;
+    private final String name;
 
     private Comparator<PathAttributes> comparator;
-    private Future<Void> cf;
     private BGPSession session;
 
-    public BGPPeer(final String name, final InetSocketAddress address, final String password, final BGPSessionPreferences prefs,
-            final AsNumber remoteAs, final RIB rib) {
+    public BGPPeer(final String name, final RIB rib) {
         this.rib = Preconditions.checkNotNull(rib);
-        this.name = Preconditions.checkNotNull(name);
-
-        final KeyMapping keys;
-        if (password != null) {
-            keys = new KeyMapping();
-            keys.put(address.getAddress(), password.getBytes(Charsets.US_ASCII));
-        } else {
-            keys = null;
-        }
-
-        this.cf = rib.getDispatcher().createReconnectingClient(address, prefs, remoteAs, this, rib.getTcpStrategyFactory(),
-                rib.getSessionStrategyFactory(), keys);
+        this.name = name;
     }
 
     @Override
     public synchronized void close() {
-        if (this.cf != null) {
-            this.cf.cancel(true);
-            if (this.session != null) {
-                this.session.close();
-                this.session = null;
-            }
-            this.cf = null;
-        }
+        dropConnection();
+        // TODO should this perform cleanup ?
     }
 
     @Override
@@ -96,7 +69,7 @@ public final class BGPPeer implements BGPSessionListener, Peer, AutoCloseable {
         LOG.info("Session with peer {} went up with tables: {}", this.name, session.getAdvertisedTableTypes());
 
         this.session = session;
-        this.comparator = new BGPObjectComparator(this.rib.getLocalAs(), this.rib.getBgpIdentifier(), session.getBgpId());
+        this.comparator = new BGPObjectComparator(this.rib.getLocalAs(), rib.getBgpIdentifier(), session.getBgpId());
 
         for (final BgpTableType t : session.getAdvertisedTableTypes()) {
             final TablesKey key = new TablesKey(t.getAfi(), t.getSafi());
@@ -149,4 +122,21 @@ public final class BGPPeer implements BGPSessionListener, Peer, AutoCloseable {
     public Comparator<PathAttributes> getComparator() {
         return this.comparator;
     }
+
+    protected RIB getRib() {
+        return rib;
+    }
+
+    @Override
+    public void releaseConnection() {
+        dropConnection();
+        cleanup();
+    }
+
+    private void dropConnection() {
+        if (this.session != null) {
+            this.session.close();
+            this.session = null;
+        }
+    }
 }
index 7b5e30f0d8fd8ae72f9d83f4feaa97234ea61df8..c0a8fe80dbe556c114866916b30427bcc6d56ea2 100644 (file)
@@ -109,8 +109,8 @@ public class BGPSessionImpl extends AbstractProtocolSession<Notification> implem
     private final AsNumber asNumber;
     private final Ipv4Address bgpId;
 
-    BGPSessionImpl(final Timer timer, final BGPSessionListener listener, final Channel channel, final Open remoteOpen,
-            final int localHoldTimer) {
+    public BGPSessionImpl(final Timer timer, final BGPSessionListener listener, final Channel channel, final Open remoteOpen,
+                          final int localHoldTimer) {
         this.listener = Preconditions.checkNotNull(listener);
         this.stateTimer = Preconditions.checkNotNull(timer);
         this.channel = Preconditions.checkNotNull(channel);
diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/StrictBGPPeerRegistry.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/StrictBGPPeerRegistry.java
new file mode 100644 (file)
index 0000000..b3abe6a
--- /dev/null
@@ -0,0 +1,234 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.protocol.bgp.rib.impl;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Maps;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.util.Map;
+import javax.annotation.concurrent.GuardedBy;
+import javax.annotation.concurrent.ThreadSafe;
+import org.opendaylight.protocol.bgp.parser.BGPDocumentedException;
+import org.opendaylight.protocol.bgp.parser.BGPError;
+import org.opendaylight.protocol.bgp.parser.BGPSessionListener;
+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.ReusableBGPPeer;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv6Address;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * BGP peer registry that allows only 1 session per BGP peer.
+ * If second session with peer is established, one of the sessions will be dropped.
+ * The session with lower source BGP id will be dropped.
+ */
+@ThreadSafe
+public final class StrictBGPPeerRegistry implements BGPPeerRegistry {
+
+    private static final Logger LOG = LoggerFactory.getLogger(StrictBGPPeerRegistry.class);
+
+    // TODO remove backwards compatibility
+    public static StrictBGPPeerRegistry GLOBAL = new StrictBGPPeerRegistry();
+
+    @GuardedBy("this")
+    private final Map<IpAddress, ReusableBGPPeer> peers = Maps.newHashMap();
+    @GuardedBy("this")
+    private final Map<IpAddress, BGPSessionId> sessionIds = Maps.newHashMap();
+    @GuardedBy("this")
+    private final Map<IpAddress, BGPSessionPreferences> peerPreferences = Maps.newHashMap();
+
+    @Override
+    public synchronized void addPeer(final IpAddress ip, final ReusableBGPPeer peer, final BGPSessionPreferences preferences) {
+        Preconditions.checkNotNull(ip);
+        Preconditions.checkArgument(peers.containsKey(ip) == false, "Peer for %s already present", ip);
+        peers.put(ip, Preconditions.checkNotNull(peer));
+        peerPreferences.put(ip, Preconditions.checkNotNull(preferences));
+    }
+
+    @Override
+    public synchronized void removePeer(final IpAddress ip) {
+        Preconditions.checkNotNull(ip);
+        peers.remove(ip);
+    }
+
+    @Override
+    public boolean isPeerConfigured(final IpAddress ip) {
+        Preconditions.checkNotNull(ip);
+        return peers.containsKey(ip);
+    }
+
+    private void checkPeerConfigured(final IpAddress ip) {
+        Preconditions.checkState(isPeerConfigured(ip), "BGP peer with ip: %s not configured, configured peers are: %s", ip, peers.keySet());
+    }
+
+    @Override
+    public synchronized BGPSessionListener getPeer(final IpAddress ip,
+            final Ipv4Address sourceId, final Ipv4Address remoteId)
+            throws BGPDocumentedException {
+        Preconditions.checkNotNull(ip);
+        Preconditions.checkNotNull(sourceId);
+        Preconditions.checkNotNull(remoteId);
+
+        checkPeerConfigured(ip);
+
+        final BGPSessionId currentConnection = new BGPSessionId(sourceId, remoteId);
+
+        if (sessionIds.containsKey(ip)) {
+            LOG.warn("Duplicate BGP session established with {}", ip);
+
+            final BGPSessionId previousConnection = sessionIds.get(ip);
+
+            // Session reestablished with different ids
+            if (previousConnection.equals(currentConnection) == false) {
+                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);
+                peers.get(ip).releaseConnection();
+                return 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);
+            }
+        }
+
+        // Map session id to peer IP address
+        sessionIds.put(ip, currentConnection);
+        return peers.get(ip);
+    }
+
+    @Override
+    public BGPSessionPreferences getPeerPreferences(final IpAddress ip) {
+        Preconditions.checkNotNull(ip);
+        checkPeerConfigured(ip);
+        return peerPreferences.get(ip);
+    }
+
+    /**
+     * Create IpAddress from SocketAddress. Only InetSocketAddress is accepted with inner address: Inet4Address and Inet6Address.
+     *
+     * @throws IllegalArgumentException if submitted socket address is not InetSocketAddress[ipv4 | ipv6]
+     * @param socketAddress socket address to transform
+     */
+    public static IpAddress getIpAddress(final SocketAddress socketAddress) {
+        Preconditions.checkNotNull(socketAddress);
+        Preconditions.checkArgument(socketAddress instanceof InetSocketAddress, "Expecting InetSocketAddress but was %s", socketAddress.getClass());
+        final InetAddress inetAddress = ((InetSocketAddress) socketAddress).getAddress();
+
+        if(inetAddress instanceof Inet4Address) {
+            return new IpAddress(new Ipv4Address(inetAddress.getHostAddress()));
+        } else if(inetAddress instanceof Inet6Address) {
+            return new IpAddress(new Ipv6Address(inetAddress.getHostAddress()));
+        }
+
+        throw new IllegalArgumentException("Expecting " + Inet4Address.class + " or " + Inet6Address.class + " but was " + inetAddress.getClass());
+    }
+
+    @Override
+    public synchronized void close() throws Exception {
+        peers.clear();
+        sessionIds.clear();
+    }
+
+    @Override
+    public String toString() {
+        return Objects.toStringHelper(this)
+                .add("peers", peers.keySet())
+                .toString();
+    }
+
+    /**
+     * 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) {
+            this.from = Preconditions.checkNotNull(from);
+            this.to = Preconditions.checkNotNull(to);
+        }
+
+        /**
+         * Equals does not take direction of connection into account id1 -> id2 and id2 -> id1 are equal
+         */
+        @Override
+        public boolean equals(final Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+
+            final BGPSessionId BGPSessionId = (BGPSessionId) o;
+
+            if (!from.equals(BGPSessionId.from) && !from.equals(BGPSessionId.to)) return false;
+            if (!to.equals(BGPSessionId.to) && !to.equals(BGPSessionId.from)) return false;
+
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = from.hashCode() + to.hashCode();
+            result = 31 * result;
+            return result;
+        }
+
+        /**
+         * 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) == false, "Equal sessions with same direction");
+            return toLong(from) > toLong(other.from);
+        }
+
+        private long toLong(final Ipv4Address from) {
+            return Long.valueOf(from.getValue().replaceAll("[^0-9]", ""));
+        }
+
+        /**
+         * 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 from.equals(other.from);
+        }
+
+        @Override
+        public String toString() {
+            return Objects.toStringHelper(this)
+                    .add("from", from)
+                    .add("to", to)
+                    .toString();
+        }
+    }
+}
diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/client/BGPClientSessionNegotiator.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/client/BGPClientSessionNegotiator.java
new file mode 100644 (file)
index 0000000..ff879af
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.protocol.bgp.rib.impl.client;
+
+import io.netty.channel.Channel;
+import io.netty.util.Timer;
+import io.netty.util.concurrent.Promise;
+import org.opendaylight.protocol.bgp.rib.impl.AbstractBGPSessionNegotiator;
+import org.opendaylight.protocol.bgp.rib.impl.BGPSessionImpl;
+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.BGPSessionValidator;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.Open;
+
+/**
+ * Client negotiator. Validates established connections using BGPClientSessionValidator
+ */
+public final class BGPClientSessionNegotiator extends AbstractBGPSessionNegotiator {
+
+    public BGPClientSessionNegotiator(final Timer timer,
+                                      final Promise<BGPSessionImpl> promise, final Channel channel,
+                                      final BGPPeerRegistry registry,
+                                      final BGPSessionValidator sessionValidator) {
+        super(timer, promise, channel, registry, sessionValidator);
+    }
+
+    protected Ipv4Address getDestinationId(final Open openMsg,
+            final BGPSessionPreferences preferences) {
+        return preferences.getBgpId();
+    }
+
+    protected Ipv4Address getSourceId(final Open openMsg,
+            final BGPSessionPreferences preferences) {
+        return openMsg.getBgpIdentifier();
+    }
+}
similarity index 60%
rename from bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/BGPSessionNegotiatorFactory.java
rename to bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/client/BGPClientSessionNegotiatorFactory.java
index ce9c2073520b67024d63137870d79e42a6f7c77b..a7bbb484d4191029740041b6580676636566a53a 100644 (file)
@@ -5,36 +5,35 @@
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
-package org.opendaylight.protocol.bgp.rib.impl;
+package org.opendaylight.protocol.bgp.rib.impl.client;
 
 import com.google.common.base.Preconditions;
-
 import io.netty.channel.Channel;
 import io.netty.util.Timer;
 import io.netty.util.concurrent.Promise;
-
 import org.opendaylight.protocol.bgp.parser.BGPSessionListener;
-import org.opendaylight.protocol.bgp.rib.impl.spi.BGPSessionPreferences;
+import org.opendaylight.protocol.bgp.rib.impl.BGPSessionImpl;
+import org.opendaylight.protocol.bgp.rib.impl.spi.BGPPeerRegistry;
 import org.opendaylight.protocol.framework.SessionListenerFactory;
 import org.opendaylight.protocol.framework.SessionNegotiator;
 import org.opendaylight.protocol.framework.SessionNegotiatorFactory;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.AsNumber;
 import org.opendaylight.yangtools.yang.binding.Notification;
 
-public final class BGPSessionNegotiatorFactory implements SessionNegotiatorFactory<Notification, BGPSessionImpl, BGPSessionListener> {
-    private final BGPSessionPreferences initialPrefs;
-    private final AsNumber remoteAs;
+public final class BGPClientSessionNegotiatorFactory implements SessionNegotiatorFactory<Notification, BGPSessionImpl, BGPSessionListener> {
+    private final BGPClientSessionValidator validator;
     private final Timer timer;
+    private final BGPPeerRegistry peerRegistry;
 
-    public BGPSessionNegotiatorFactory(final Timer timer, final BGPSessionPreferences initialPrefs, final AsNumber remoteAs) {
+    public BGPClientSessionNegotiatorFactory(final Timer timer, final AsNumber remoteAs, final BGPPeerRegistry peerRegistry) {
+        this.peerRegistry = peerRegistry;
         this.timer = Preconditions.checkNotNull(timer);
-        this.initialPrefs = Preconditions.checkNotNull(initialPrefs);
-        this.remoteAs = Preconditions.checkNotNull(remoteAs);
+        this.validator = new BGPClientSessionValidator(remoteAs, peerRegistry);
     }
 
     @Override
     public SessionNegotiator<BGPSessionImpl> getSessionNegotiator(final SessionListenerFactory<BGPSessionListener> factory,
             final Channel channel, final Promise<BGPSessionImpl> promise) {
-        return new BGPSessionNegotiator(this.timer, promise, channel, this.initialPrefs, remoteAs, factory.getSessionListener());
+        return new BGPClientSessionNegotiator(this.timer, promise, channel, peerRegistry, validator);
     }
 }
diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/client/BGPClientSessionValidator.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/client/BGPClientSessionValidator.java
new file mode 100644 (file)
index 0000000..7a3b3d8
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.protocol.bgp.rib.impl.client;
+
+import java.util.List;
+import org.opendaylight.protocol.bgp.parser.AsNumberUtil;
+import org.opendaylight.protocol.bgp.parser.BGPDocumentedException;
+import org.opendaylight.protocol.bgp.parser.BGPError;
+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.BGPSessionValidator;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.AsNumber;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.Open;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.open.BgpParameters;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Validates Bgp sessions established from current device to remote.
+ */
+public class BGPClientSessionValidator implements BGPSessionValidator {
+
+    private static final Logger LOG = LoggerFactory.getLogger(BGPClientSessionValidator.class);
+
+    private final AsNumber remoteAs;
+    private final BGPPeerRegistry peerRegistry;
+
+    public BGPClientSessionValidator(final AsNumber remoteAs, final BGPPeerRegistry peerRegistry) {
+        this.remoteAs = remoteAs;
+        this.peerRegistry = peerRegistry;
+    }
+
+    /**
+     * Validates with exception:
+     * <ul>
+     * <li>correct remote AS attribute</li>
+     * <li>non empty BgpParameters collection</li>
+     * </ul>
+     *
+     * Validates with log message:
+     * <ul>
+     * <li>local BgpParameters are superset of remote BgpParameters</li>
+     * </ul>
+     */
+    @Override
+    public void validate(final Open openObj, final BGPSessionPreferences localPref) throws BGPDocumentedException {
+        final AsNumber as = AsNumberUtil.advertizedAsNumber(openObj);
+        if (!this.remoteAs.equals(as)) {
+            LOG.warn("Unexpected remote AS number. Expecting {}, got {}", this.remoteAs, as);
+            throw new BGPDocumentedException("Peer AS number mismatch", BGPError.BAD_PEER_AS);
+        }
+
+        final List<BgpParameters> prefs = openObj.getBgpParameters();
+        if (prefs != null && !prefs.isEmpty()) {
+            if (!prefs.containsAll(localPref.getParams())) {
+                LOG.info("BGP Open message session parameters differ, session still accepted.");
+            }
+        } else {
+            throw new BGPDocumentedException("Open message unacceptable. Check the configuration of BGP speaker.", BGPError.UNSPECIFIC_OPEN_ERROR);
+        }
+    }
+}
diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/server/BGPServerSessionNegotiator.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/server/BGPServerSessionNegotiator.java
new file mode 100644 (file)
index 0000000..e21ca40
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.protocol.bgp.rib.impl.server;
+
+import io.netty.channel.Channel;
+import io.netty.util.Timer;
+import io.netty.util.concurrent.Promise;
+
+import org.opendaylight.protocol.bgp.rib.impl.AbstractBGPSessionNegotiator;
+import org.opendaylight.protocol.bgp.rib.impl.BGPSessionImpl;
+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.BGPSessionValidator;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.Open;
+
+/**
+ * Server negotiator. Validates established connections using BGPServerSessionValidator
+ */
+public final class BGPServerSessionNegotiator extends AbstractBGPSessionNegotiator {
+
+    public BGPServerSessionNegotiator(final Timer timer,
+                                      final Promise<BGPSessionImpl> promise, final Channel channel,
+                                      final BGPPeerRegistry registry,
+                                      final BGPSessionValidator sessionValidator) {
+        super(timer, promise, channel, registry, sessionValidator);
+    }
+
+    protected Ipv4Address getSourceId(final Open openMsg,
+            final BGPSessionPreferences preferences) {
+        return preferences.getBgpId();
+    }
+
+    protected Ipv4Address getDestinationId(final Open openMsg,
+            final BGPSessionPreferences preferences) {
+        return openMsg.getBgpIdentifier();
+    }
+}
diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/server/BGPServerSessionNegotiatorFactory.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/server/BGPServerSessionNegotiatorFactory.java
new file mode 100644 (file)
index 0000000..e953eda
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.protocol.bgp.rib.impl.server;
+
+import com.google.common.base.Preconditions;
+import io.netty.channel.Channel;
+import io.netty.util.Timer;
+import io.netty.util.concurrent.Promise;
+import org.opendaylight.protocol.bgp.parser.BGPSessionListener;
+import org.opendaylight.protocol.bgp.rib.impl.BGPSessionImpl;
+import org.opendaylight.protocol.bgp.rib.impl.spi.BGPPeerRegistry;
+import org.opendaylight.protocol.bgp.rib.impl.spi.BGPSessionValidator;
+import org.opendaylight.protocol.framework.SessionListenerFactory;
+import org.opendaylight.protocol.framework.SessionNegotiator;
+import org.opendaylight.protocol.framework.SessionNegotiatorFactory;
+import org.opendaylight.yangtools.yang.binding.Notification;
+
+public final class BGPServerSessionNegotiatorFactory implements SessionNegotiatorFactory<Notification, BGPSessionImpl, BGPSessionListener> {
+    private final Timer timer;
+    private final BGPSessionValidator validator;
+    private final BGPPeerRegistry registry;
+
+    public BGPServerSessionNegotiatorFactory(final Timer timer, final BGPSessionValidator sessionValidator, final BGPPeerRegistry registry) {
+        this.registry = registry;
+        this.timer = Preconditions.checkNotNull(timer);
+        this.validator = Preconditions.checkNotNull(sessionValidator);
+    }
+
+    @Override
+    public SessionNegotiator<BGPSessionImpl> getSessionNegotiator(final SessionListenerFactory<BGPSessionListener> factory,
+            final Channel channel, final Promise<BGPSessionImpl> promise) {
+        return new BGPServerSessionNegotiator(this.timer, promise, channel, registry, validator);
+    }
+}
diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/server/BGPServerSessionValidator.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/server/BGPServerSessionValidator.java
new file mode 100644 (file)
index 0000000..b0c3977
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.protocol.bgp.rib.impl.server;
+
+import org.opendaylight.protocol.bgp.parser.BGPDocumentedException;
+import org.opendaylight.protocol.bgp.rib.impl.spi.BGPSessionPreferences;
+import org.opendaylight.protocol.bgp.rib.impl.spi.BGPSessionValidator;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.Open;
+
+/**
+ * Validates Bgp sessions established from remote devices.
+ */
+public final class BGPServerSessionValidator implements BGPSessionValidator {
+
+    @Override
+    public void validate(final Open openObj, final BGPSessionPreferences localPref) throws BGPDocumentedException {
+        // No validation performed, org.opendaylight.protocol.bgp.rib.impl.AbstractBGPSessionNegotiator checks if peer is configured
+    }
+}
index e9f2dad7c3f7a91d9734b076de4e22ca2154d9ad..6d4d436779f2a2b94f39ccbb02fa95369e75ac86 100644 (file)
@@ -8,12 +8,9 @@
 package org.opendaylight.protocol.bgp.rib.impl.spi;
 
 import io.netty.util.concurrent.Future;
-
 import java.net.InetSocketAddress;
-
 import org.opendaylight.bgpcep.tcpmd5.KeyMapping;
 import org.opendaylight.protocol.bgp.parser.BGPSession;
-import org.opendaylight.protocol.bgp.parser.BGPSessionListener;
 import org.opendaylight.protocol.framework.ReconnectStrategy;
 import org.opendaylight.protocol.framework.ReconnectStrategyFactory;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.AsNumber;
@@ -21,24 +18,23 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.
 /**
  * Dispatcher class for creating BGP clients.
  */
-public interface BGPDispatcher {
+public interface BGPDispatcher extends BGPServerDispatcher {
 
     /**
      * Creates BGP client.
      *
      * @param address Peer address
-     * @param preferences connection attributes required for connection
-     * @param listener BGP message listener
+     * @param peerRegistry BGP peer registry
      * @return Future promising a client session
      */
-    Future<? extends BGPSession> createClient(InetSocketAddress address, BGPSessionPreferences preferences, AsNumber remoteAs,
-            BGPSessionListener listener, ReconnectStrategy strategy);
+    Future<? extends BGPSession> createClient(InetSocketAddress address, AsNumber remoteAs,
+            BGPPeerRegistry peerRegistry, ReconnectStrategy strategy);
 
-    Future<Void> createReconnectingClient(InetSocketAddress address, BGPSessionPreferences preferences, AsNumber remoteAs,
-            BGPSessionListener listener, ReconnectStrategyFactory connectStrategyFactory,
+    Future<Void> createReconnectingClient(InetSocketAddress address, AsNumber remoteAs,
+                                          BGPPeerRegistry peerRegistry, ReconnectStrategyFactory connectStrategyFactory,
             ReconnectStrategyFactory reestablishStrategyFactory);
 
-    Future<Void> createReconnectingClient(InetSocketAddress address, BGPSessionPreferences preferences, AsNumber remoteAs,
-            BGPSessionListener listener, ReconnectStrategyFactory connectStrategyFactory,
+    Future<Void> createReconnectingClient(InetSocketAddress address, AsNumber remoteAs,
+                                          BGPPeerRegistry peerRegistry, ReconnectStrategyFactory connectStrategyFactory,
             ReconnectStrategyFactory reestablishStrategyFactory, KeyMapping keys);
 }
diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/spi/BGPPeerRegistry.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/spi/BGPPeerRegistry.java
new file mode 100644 (file)
index 0000000..2252d49
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.protocol.bgp.rib.impl.spi;
+
+import org.opendaylight.protocol.bgp.parser.BGPDocumentedException;
+import org.opendaylight.protocol.bgp.parser.BGPSessionListener;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
+
+/**
+ * Registry that contains configured bgp peers ready for when a bgp session is established with remote peer.
+ * IP address is uses as a key for configured peers. TODO Is IP sufficient ID for peers ?
+ */
+public interface BGPPeerRegistry extends AutoCloseable {
+
+    /**
+     * Add configured peer, its IP address and preferences. To be used when a BGP session is established.
+     *
+     * @param ip address of remote peer
+     * @param peer configured peer as ReusableBGPPeer
+     * @param prefs session preferences for configured peer
+     */
+    void addPeer(IpAddress ip, ReusableBGPPeer peer, BGPSessionPreferences prefs);
+
+    /**
+     * Remove configured peer from registry.
+     *
+     * @param ip address of remote peer
+     */
+    void removePeer(IpAddress ip);
+
+    /**
+     * Check whether peer on provided IP address is present in this registry.
+     *
+     * @param ip address of remote peer
+     * @return true if peer is present false otherwise
+     */
+    boolean isPeerConfigured(IpAddress ip);
+
+    /**
+     * Get configured peer after BGP session was successfully established. Called by negotiators.
+     *
+     * @param ip address of remote peer
+     * @param sourceId BGP ID of peer that initiated the session (current device or remote peer)
+     * @param remoteId BGP ID of peer that accepted the session (current device or remote peer)
+     * @return configured Peer as BGP listener
+     *
+     * @throws BGPDocumentedException if session establishment cannot be finished successfully
+     * @throws java.lang.IllegalStateException if there is no peer configured for provided ip address
+     */
+    BGPSessionListener getPeer(IpAddress ip, Ipv4Address sourceId, Ipv4Address remoteId) throws BGPDocumentedException;
+
+    /**
+     * @param ip address of remote peer
+     * @return BGP session preferences for configured peer
+     *
+     * @throws java.lang.IllegalStateException if there is no peer configured for provided ip address
+     */
+    BGPSessionPreferences getPeerPreferences(IpAddress ip);
+
+}
diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/spi/BGPServerDispatcher.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/spi/BGPServerDispatcher.java
new file mode 100644 (file)
index 0000000..4d252fa
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.protocol.bgp.rib.impl.spi;
+
+import io.netty.channel.ChannelFuture;
+import java.net.InetSocketAddress;
+import org.opendaylight.bgpcep.tcpmd5.KeyMapping;
+
+/**
+ * Dispatcher class for creating BGP server.
+ */
+public interface BGPServerDispatcher {
+
+    ChannelFuture createServer(BGPPeerRegistry peerRegistry, InetSocketAddress address, BGPSessionValidator sessionValidator);
+
+    /**
+     * Create new BGP server to accept incoming bgp connections (bound to provided socket address).
+     */
+    ChannelFuture createServer(BGPPeerRegistry peerRegistry, InetSocketAddress address, BGPSessionValidator sessionValidator, KeyMapping keys);
+}
diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/spi/BGPSessionValidator.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/spi/BGPSessionValidator.java
new file mode 100644 (file)
index 0000000..a55d854
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.protocol.bgp.rib.impl.spi;
+
+import org.opendaylight.protocol.bgp.parser.BGPDocumentedException;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.Open;
+
+/**
+ * BGP Session validator. Validates established sessions with bgp peers after OPEN message is received
+ */
+public interface BGPSessionValidator {
+
+    void validate(final Open openObj, final BGPSessionPreferences localPref) throws BGPDocumentedException;
+}
diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/spi/ReusableBGPPeer.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/spi/ReusableBGPPeer.java
new file mode 100644 (file)
index 0000000..7b5437c
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.protocol.bgp.rib.impl.spi;
+
+import org.opendaylight.protocol.bgp.parser.BGPSessionListener;
+
+public interface ReusableBGPPeer extends BGPSessionListener {
+
+    // TODO merge with BGPSessionListener ?
+
+    void releaseConnection();
+
+}
index 676a3a023f46664c11c36ce7b8a110d1b8e3980d..3ea8c4b4b2d830cc441792af519a52c27a68da9b 100644 (file)
@@ -15,7 +15,6 @@ import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 
 import com.google.common.collect.Lists;
-
 import io.netty.channel.Channel;
 import io.netty.channel.ChannelFuture;
 import io.netty.channel.ChannelHandler;
@@ -24,9 +23,10 @@ import io.netty.util.HashedWheelTimer;
 import io.netty.util.concurrent.DefaultPromise;
 import io.netty.util.concurrent.GenericFutureListener;
 import io.netty.util.concurrent.GlobalEventExecutor;
-
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.UnknownHostException;
 import java.util.List;
-
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Ignore;
@@ -37,8 +37,13 @@ import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 import org.opendaylight.protocol.bgp.parser.BGPError;
 import org.opendaylight.protocol.bgp.parser.BgpTableTypeImpl;
+import org.opendaylight.protocol.bgp.rib.impl.client.BGPClientSessionNegotiator;
+import org.opendaylight.protocol.bgp.rib.impl.client.BGPClientSessionValidator;
+import org.opendaylight.protocol.bgp.rib.impl.spi.BGPPeerRegistry;
 import org.opendaylight.protocol.bgp.rib.impl.spi.BGPSessionPreferences;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.AsNumber;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev131125.LinkstateAddressFamily;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev131125.LinkstateSubsequentAddressFamily;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.Keepalive;
@@ -58,7 +63,7 @@ import org.opendaylight.yangtools.yang.binding.Notification;
 
 public class FSMTest {
 
-    private BGPSessionNegotiator clientSession;
+    private BGPClientSessionNegotiator clientSession;
 
     @Mock
     private Channel speakerListener;
@@ -75,7 +80,7 @@ public class FSMTest {
     private Open classicOpen;
 
     @Before
-    public void setUp() {
+    public void setUp() throws UnknownHostException {
         MockitoAnnotations.initMocks(this);
         final List<BgpParameters> tlvs = Lists.newArrayList();
 
@@ -85,11 +90,16 @@ public class FSMTest {
         tlvs.add(new BgpParametersBuilder().setCParameters(
                 new MultiprotocolCaseBuilder().setMultiprotocolCapability(
                         new MultiprotocolCapabilityBuilder().setAfi(this.linkstatett.getAfi()).setSafi(this.linkstatett.getSafi()).build()).build()).build());
-        final BGPSessionPreferences prefs = new BGPSessionPreferences(new AsNumber(30L), (short) 3, null, tlvs);
+        final BGPSessionPreferences prefs = new BGPSessionPreferences(new AsNumber(30L), (short) 3, new Ipv4Address("1.1.1.1"), tlvs);
 
         final ChannelFuture f = mock(ChannelFuture.class);
         doReturn(null).when(f).addListener(any(GenericFutureListener.class));
-        this.clientSession = new BGPSessionNegotiator(new HashedWheelTimer(), new DefaultPromise<BGPSessionImpl>(GlobalEventExecutor.INSTANCE), this.speakerListener, prefs, new AsNumber(30L), new SimpleSessionListener());
+
+        final InetAddress peerAddress = InetAddress.getByName("1.1.1.2");
+        final BGPPeerRegistry peerRegistry = new StrictBGPPeerRegistry();
+        peerRegistry.addPeer(new IpAddress(new Ipv4Address(peerAddress.getHostAddress())), new SimpleSessionListener(), prefs);
+
+        this.clientSession = new BGPClientSessionNegotiator(new HashedWheelTimer(), new DefaultPromise<BGPSessionImpl>(GlobalEventExecutor.INSTANCE), this.speakerListener, peerRegistry, new BGPClientSessionValidator(new AsNumber(30L), peerRegistry));
         doAnswer(new Answer<Object>() {
             @Override
             public Object answer(final InvocationOnMock invocation) {
@@ -98,12 +108,14 @@ public class FSMTest {
                 return f;
             }
         }).when(this.speakerListener).writeAndFlush(any(Notification.class));
+
         doReturn("TestingChannel").when(this.speakerListener).toString();
+        doReturn(new InetSocketAddress(peerAddress, 179)).when(this.speakerListener).remoteAddress();
         doReturn(this.pipeline).when(this.speakerListener).pipeline();
         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.classicOpen = new OpenBuilder().setMyAsNumber(30).setHoldTimer(3).setVersion(new ProtocolVersion((short) 4)).setBgpParameters(
-                tlvs).build();
+                tlvs).setBgpIdentifier(new Ipv4Address("1.1.1.2")).build();
     }
 
     @Test
@@ -115,7 +127,7 @@ public class FSMTest {
         assertEquals(2, this.receivedMsgs.size());
         assertTrue(this.receivedMsgs.get(1) instanceof Keepalive);
         this.clientSession.handleMessage(new KeepaliveBuilder().build());
-        assertEquals(this.clientSession.getState(), BGPSessionNegotiator.State.Finished);
+        assertEquals(this.clientSession.getState(), BGPClientSessionNegotiator.State.Finished);
         Thread.sleep(1000);
         Thread.sleep(100);
         assertEquals(3, this.receivedMsgs.size());
@@ -141,7 +153,7 @@ public class FSMTest {
         this.clientSession.channelActive(null);
         assertEquals(1, this.receivedMsgs.size());
         assertTrue(this.receivedMsgs.get(0) instanceof Open);
-        Thread.sleep(BGPSessionNegotiator.INITIAL_HOLDTIMER * 1000 * 60);
+        Thread.sleep(BGPClientSessionNegotiator.INITIAL_HOLDTIMER * 1000 * 60);
         Thread.sleep(100);
         final Notification m = this.receivedMsgs.get(this.receivedMsgs.size() - 1);
         assertEquals(BGPError.HOLD_TIMER_EXPIRED, BGPError.forValue(((Notify) m).getErrorCode(), ((Notify) m).getErrorSubcode()));
@@ -152,7 +164,7 @@ public class FSMTest {
         this.clientSession.channelActive(null);
         this.clientSession.handleMessage(this.classicOpen);
         this.clientSession.handleMessage(new KeepaliveBuilder().build());
-        assertEquals(this.clientSession.getState(), BGPSessionNegotiator.State.Finished);
+        assertEquals(this.clientSession.getState(), BGPClientSessionNegotiator.State.Finished);
         this.clientSession.handleMessage(new OpenBuilder().setMyAsNumber(30).setHoldTimer(3).setVersion(new ProtocolVersion((short) 4)).build());
         assertEquals(3, this.receivedMsgs.size());
         assertTrue(this.receivedMsgs.get(2) instanceof Notify);
index 748620d0d8a4bfd35f50ec76f2804b44bdddcbb1..732a5b7dbfa80cf6dc7f8b8804b0e32f5a222603 100644 (file)
@@ -8,12 +8,10 @@
 package org.opendaylight.protocol.bgp.rib.impl;
 
 import com.google.common.collect.Lists;
-
 import java.util.List;
-
 import org.opendaylight.protocol.bgp.parser.BGPSession;
-import org.opendaylight.protocol.bgp.parser.BGPSessionListener;
 import org.opendaylight.protocol.bgp.parser.BGPTerminationReason;
+import org.opendaylight.protocol.bgp.rib.impl.spi.ReusableBGPPeer;
 import org.opendaylight.yangtools.yang.binding.Notification;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -21,7 +19,7 @@ import org.slf4j.LoggerFactory;
 /**
  * Listener for the client.
  */
-public class SimpleSessionListener implements BGPSessionListener {
+public class SimpleSessionListener implements ReusableBGPPeer {
 
     private final List<Notification> listMsg = Lists.newArrayList();
 
@@ -57,4 +55,9 @@ public class SimpleSessionListener implements BGPSessionListener {
     public void onSessionTerminated(final BGPSession session, final BGPTerminationReason cause) {
         LOG.debug("Session terminated. Cause : {}", cause.toString());
     }
+
+    @Override
+    public void releaseConnection() {
+        LOG.debug("Releasing connection");
+    }
 }
diff --git a/bgp/rib-impl/src/test/java/org/opendaylight/protocol/bgp/rib/impl/StrictBGPPeerRegistryTest.java b/bgp/rib-impl/src/test/java/org/opendaylight/protocol/bgp/rib/impl/StrictBGPPeerRegistryTest.java
new file mode 100644 (file)
index 0000000..1d9b1ed
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.protocol.bgp.rib.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.fail;
+
+import java.net.InetSocketAddress;
+
+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.BGPSessionListener;
+import org.opendaylight.protocol.bgp.rib.impl.spi.BGPSessionPreferences;
+import org.opendaylight.protocol.bgp.rib.impl.spi.ReusableBGPPeer;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
+
+public class StrictBGPPeerRegistryTest {
+
+    private StrictBGPPeerRegistry droppingBGPSessionRegistry;
+    private BGPSessionPreferences mockPreferences;
+
+    @Before
+    public void setUp() throws Exception {
+        droppingBGPSessionRegistry = new StrictBGPPeerRegistry();
+        mockPreferences = getMockPreferences();
+    }
+
+    @Test
+    public void testIpAddressConstruction() throws Exception {
+        final InetSocketAddress adr = new InetSocketAddress("127.0.0.1", 179);
+        final IpAddress ipAdr = StrictBGPPeerRegistry.getIpAddress(adr);
+        assertEquals("127.0.0.1", ipAdr.getIpv4Address().getValue());
+    }
+
+    @Test
+    public void testDuplicate() 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();
+        droppingBGPSessionRegistry.addPeer(remoteIp, session1, mockPreferences);
+
+        droppingBGPSessionRegistry.getPeer(remoteIp, from, to);
+        try {
+            droppingBGPSessionRegistry.getPeer(remoteIp, from, to);
+        } catch (final IllegalStateException e) {
+            Mockito.verifyZeroInteractions(session1);
+            return;
+        }
+
+        fail("Same peer cannot be connected twice");
+    }
+
+    @Test
+    public void testNotAllowed() 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");
+
+        try {
+            droppingBGPSessionRegistry.getPeer(remoteIp, from, to);
+        } catch (final IllegalStateException e) {
+            return;
+        }
+        fail("Unknown peer cannot be connected");
+    }
+
+    @Test
+    public void testOk() throws Exception {
+        final Ipv4Address from = new Ipv4Address("0.0.0.1");
+
+        final Ipv4Address to = new Ipv4Address("255.255.255.255");
+        final IpAddress remoteIp = new IpAddress(to);
+        final Ipv4Address to2 = new Ipv4Address("255.255.255.254");
+        final IpAddress remoteIp2 = new IpAddress(to2);
+
+        final ReusableBGPPeer session1 = getMockSession();
+        droppingBGPSessionRegistry.addPeer(remoteIp, session1, mockPreferences);
+        final ReusableBGPPeer session2 = getMockSession();
+        droppingBGPSessionRegistry.addPeer(remoteIp2, session2, mockPreferences);
+
+        final BGPSessionListener returnedSession1 = droppingBGPSessionRegistry.getPeer(remoteIp, from, to);
+        assertSame(session1, returnedSession1);
+        final BGPSessionListener returnedSession2 = droppingBGPSessionRegistry.getPeer(remoteIp2, from, to2);
+        assertSame(session2, returnedSession2);
+
+        Mockito.verifyZeroInteractions(session1);
+        Mockito.verifyZeroInteractions(session2);
+    }
+
+    @Test
+    public void testDropSecond() throws Exception {
+        final Ipv4Address higher = new Ipv4Address("192.168.200.200");
+        final Ipv4Address lower = new Ipv4Address("10.10.10.10");
+        final IpAddress remoteIp = new IpAddress(lower);
+
+        final ReusableBGPPeer session1 = getMockSession();
+        droppingBGPSessionRegistry.addPeer(remoteIp, session1, mockPreferences);
+
+        droppingBGPSessionRegistry.getPeer(remoteIp, higher, lower);
+        try {
+            droppingBGPSessionRegistry.getPeer(remoteIp, lower, higher);
+        } catch (final BGPDocumentedException e) {
+            Mockito.verifyZeroInteractions(session1);
+            return;
+        }
+
+        fail("Same peer cannot be connected twice");
+    }
+
+    @Test
+    public void testDropFirst() throws Exception {
+        final Ipv4Address higher = new Ipv4Address("123.123.123.123");
+        final Ipv4Address lower = new Ipv4Address("123.123.123.122");
+        final IpAddress remoteIp = new IpAddress(lower);
+
+        final ReusableBGPPeer session1 = getMockSession();
+        droppingBGPSessionRegistry.addPeer(remoteIp, session1, mockPreferences);
+
+        droppingBGPSessionRegistry.getPeer(remoteIp, lower, higher);
+        droppingBGPSessionRegistry.getPeer(remoteIp, higher, lower);
+        Mockito.verify(session1).releaseConnection();
+    }
+
+    private ReusableBGPPeer getMockSession() {
+        final ReusableBGPPeer mock = Mockito.mock(ReusableBGPPeer.class);
+        Mockito.doNothing().when(mock).releaseConnection();
+        return mock;
+    }
+
+    public BGPSessionPreferences getMockPreferences() {
+        return new BGPSessionPreferences(null, 1, null, null);
+    }
+}
index 7d5e4b3d79637b6b5eaa2000ffbfd6992cf59b38..129039c82335bb0bf1fab7fc2927b404b5e17657 100644 (file)
@@ -10,18 +10,17 @@ package org.opendaylight.protocol.bgp.testtool;
 import io.netty.channel.nio.NioEventLoopGroup;
 import io.netty.util.HashedWheelTimer;
 import io.netty.util.concurrent.GlobalEventExecutor;
-
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.util.HashMap;
 import java.util.Map;
-
-import org.opendaylight.protocol.bgp.parser.BGPSessionListener;
 import org.opendaylight.protocol.bgp.parser.impl.BGPActivator;
 import org.opendaylight.protocol.bgp.parser.spi.pojo.ServiceLoaderBGPExtensionProviderContext;
 import org.opendaylight.protocol.bgp.rib.impl.BGPDispatcherImpl;
 import org.opendaylight.protocol.bgp.rib.impl.BGPSessionProposalImpl;
+import org.opendaylight.protocol.bgp.rib.impl.StrictBGPPeerRegistry;
 import org.opendaylight.protocol.bgp.rib.impl.spi.BGPSessionPreferences;
+import org.opendaylight.protocol.bgp.rib.impl.spi.ReusableBGPPeer;
 import org.opendaylight.protocol.framework.NeverReconnectStrategy;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.AsNumber;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
@@ -99,7 +98,7 @@ public final class Main {
 
         final Main m = new Main();
 
-        final BGPSessionListener sessionListener = new TestingListener();
+        final ReusableBGPPeer sessionListener = new TestingListener();
 
         final Map<Class<? extends AddressFamily>, Class<? extends SubsequentAddressFamily>> tables = new HashMap<>();
         tables.put(Ipv4AddressFamily.class, UnicastSubsequentAddressFamily.class);
@@ -112,7 +111,10 @@ public final class Main {
         LOG.debug("{} {} {}", address, sessionListener, proposal);
 
         final InetSocketAddress addr = address;
-        m.dispatcher.createClient(addr, proposal, as, sessionListener,
+        final StrictBGPPeerRegistry strictBGPPeerRegistry = new StrictBGPPeerRegistry();
+        strictBGPPeerRegistry.addPeer(StrictBGPPeerRegistry.getIpAddress(address), sessionListener, proposal);
+
+        m.dispatcher.createClient(addr, as, strictBGPPeerRegistry,
                 new NeverReconnectStrategy(GlobalEventExecutor.INSTANCE, RECONNECT_MILLIS));
     }
 }
index 47088e8a0e57e46021f70dc9695e131c8ad7bc7d..587c4a48d0f9c07ab15cf885c033c598b460e85e 100644 (file)
@@ -8,8 +8,8 @@
 package org.opendaylight.protocol.bgp.testtool;
 
 import org.opendaylight.protocol.bgp.parser.BGPSession;
-import org.opendaylight.protocol.bgp.parser.BGPSessionListener;
 import org.opendaylight.protocol.bgp.parser.BGPTerminationReason;
+import org.opendaylight.protocol.bgp.rib.impl.spi.ReusableBGPPeer;
 import org.opendaylight.yangtools.yang.binding.Notification;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -17,7 +17,7 @@ import org.slf4j.LoggerFactory;
 /**
  * Testing BGP Listener.
  */
-public class TestingListener implements BGPSessionListener {
+public class TestingListener implements ReusableBGPPeer {
     private static final Logger LOG = LoggerFactory.getLogger(TestingListener.class);
 
     @Override
@@ -40,4 +40,9 @@ public class TestingListener implements BGPSessionListener {
     public void onSessionTerminated(final BGPSession session, final BGPTerminationReason cause) {
         LOG.info("Client Listener: Connection lost: {}.", cause);
     }
+
+    @Override
+    public void releaseConnection() {
+        LOG.info("Client Listener: Connection released.");
+    }
 }
index cf664521f8541d4c45452c09056ec76bbf94f3b5..7a93591617d29a2a092428825f7966e9c199a635 100644 (file)
@@ -8,34 +8,36 @@
 package org.opendaylight.protocol.bgp.testtool;
 
 import com.google.common.base.Preconditions;
-
 import io.netty.channel.nio.NioEventLoopGroup;
 import io.netty.channel.socket.SocketChannel;
 import io.netty.util.HashedWheelTimer;
 import io.netty.util.concurrent.DefaultPromise;
 import io.netty.util.concurrent.GlobalEventExecutor;
 import io.netty.util.concurrent.Promise;
-
 import java.net.InetSocketAddress;
 import java.util.HashMap;
 import java.util.Map;
-
+import org.opendaylight.protocol.bgp.parser.BGPDocumentedException;
 import org.opendaylight.protocol.bgp.parser.BGPSessionListener;
 import org.opendaylight.protocol.bgp.parser.spi.pojo.ServiceLoaderBGPExtensionProviderContext;
 import org.opendaylight.protocol.bgp.rib.impl.BGPHandlerFactory;
 import org.opendaylight.protocol.bgp.rib.impl.BGPSessionImpl;
-import org.opendaylight.protocol.bgp.rib.impl.BGPSessionNegotiatorFactory;
 import org.opendaylight.protocol.bgp.rib.impl.BGPSessionProposalImpl;
+import org.opendaylight.protocol.bgp.rib.impl.server.BGPServerSessionNegotiatorFactory;
+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.BGPSessionValidator;
+import org.opendaylight.protocol.bgp.rib.impl.spi.ReusableBGPPeer;
 import org.opendaylight.protocol.framework.AbstractDispatcher;
 import org.opendaylight.protocol.framework.ProtocolSession;
 import org.opendaylight.protocol.framework.SessionListener;
-import org.opendaylight.protocol.framework.SessionListenerFactory;
 import org.opendaylight.protocol.framework.SessionNegotiatorFactory;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.AsNumber;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev131125.LinkstateAddressFamily;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev131125.LinkstateSubsequentAddressFamily;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.Open;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.AddressFamily;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.Ipv4AddressFamily;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.SubsequentAddressFamily;
@@ -54,38 +56,61 @@ public class BGPSpeakerMock<M, S extends ProtocolSession<M>, L extends SessionLi
         this.factory = Preconditions.checkNotNull(factory);
     }
 
-    public void createServer(final InetSocketAddress address, final SessionListenerFactory<L> listenerFactory) {
+    public void createServer(final InetSocketAddress address) {
         super.createServer(address, new PipelineInitializer<S>() {
 
             @Override
             public void initializeChannel(final SocketChannel ch, final Promise<S> promise) {
                 ch.pipeline().addLast(BGPSpeakerMock.this.factory.getDecoders());
                 ch.pipeline().addLast("negotiator",
-                        BGPSpeakerMock.this.negotiatorFactory.getSessionNegotiator(listenerFactory, ch, promise));
+                        BGPSpeakerMock.this.negotiatorFactory.getSessionNegotiator(null, ch, promise));
                 ch.pipeline().addLast(BGPSpeakerMock.this.factory.getEncoders());
             }
         });
     }
 
     public static void main(final String[] args) throws Exception {
+        final Map<Class<? extends AddressFamily>, Class<? extends SubsequentAddressFamily>> tables = new HashMap<>();
+        tables.put(Ipv4AddressFamily.class, UnicastSubsequentAddressFamily.class);
+        tables.put(LinkstateAddressFamily.class, LinkstateSubsequentAddressFamily.class);
+
+        final BGPPeerRegistry peerRegistry = new BGPPeerRegistry() {
+            @Override
+            public void addPeer(final IpAddress ip, final ReusableBGPPeer peer, final BGPSessionPreferences prefs) {}
+
+            @Override
+            public void removePeer(final IpAddress ip) {}
+
+            @Override
+            public boolean isPeerConfigured(final IpAddress ip) {
+                return true;
+            }
 
-        final SessionListenerFactory<BGPSessionListener> f = new SessionListenerFactory<BGPSessionListener>() {
             @Override
-            public BGPSessionListener getSessionListener() {
+            public BGPSessionListener getPeer(final IpAddress ip, final Ipv4Address sourceId, final Ipv4Address remoteId) throws BGPDocumentedException {
                 return new SpeakerSessionListener();
             }
-        };
 
-        final Map<Class<? extends AddressFamily>, Class<? extends SubsequentAddressFamily>> tables = new HashMap<>();
-        tables.put(Ipv4AddressFamily.class, UnicastSubsequentAddressFamily.class);
-        tables.put(LinkstateAddressFamily.class, LinkstateSubsequentAddressFamily.class);
+            @Override
+            public BGPSessionPreferences getPeerPreferences(final IpAddress ip) {
+                return new BGPSessionProposalImpl((short) 90, new AsNumber(72L), new Ipv4Address("127.0.0.2"), tables).getProposal();
+            }
 
-        final BGPSessionPreferences prefs = new BGPSessionProposalImpl((short) 90, new AsNumber(72L), new Ipv4Address("127.0.0.2"), tables).getProposal();
+            @Override
+            public void close() throws Exception {
+
+            }
+        };
 
-        final SessionNegotiatorFactory<Notification, BGPSessionImpl, BGPSessionListener> snf = new BGPSessionNegotiatorFactory(new HashedWheelTimer(), prefs, new AsNumber(72L));
+        final SessionNegotiatorFactory<Notification, BGPSessionImpl, BGPSessionListener> snf = new BGPServerSessionNegotiatorFactory(new HashedWheelTimer(), new BGPSessionValidator() {
+            @Override
+            public void validate(final Open openObj, final BGPSessionPreferences prefs) throws BGPDocumentedException {
+                // NOOP
+            }
+        }, peerRegistry);
 
         final BGPSpeakerMock<Notification, BGPSessionImpl, BGPSessionListener> mock = new BGPSpeakerMock<>(snf, new BGPHandlerFactory(ServiceLoaderBGPExtensionProviderContext.getSingletonInstance().getMessageRegistry()), new DefaultPromise<BGPSessionImpl>(GlobalEventExecutor.INSTANCE));
 
-        mock.createServer(new InetSocketAddress("127.0.0.2", 12345), f);
+        mock.createServer(new InetSocketAddress("127.0.0.2", 12345));
     }
 }
index 13c1c69de19e6d21cc1fc397356b26899d46ca3d..8847dd8cc3ff90094d74c3552e9313102151ecbd 100644 (file)
@@ -12,9 +12,7 @@ import com.google.common.collect.Collections2;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 import com.google.common.eventbus.EventBus;
-
 import io.netty.util.concurrent.GlobalEventExecutor;
-
 import java.net.InetSocketAddress;
 import java.util.Collection;
 import java.util.HashMap;
@@ -23,9 +21,7 @@ import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
-
 import javax.annotation.Nullable;
-
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -39,14 +35,13 @@ import org.opendaylight.bgpcep.tcpmd5.KeyMapping;
 import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
 import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
 import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
-import org.opendaylight.protocol.bgp.parser.BGPSessionListener;
 import org.opendaylight.protocol.bgp.parser.BgpTableTypeImpl;
 import org.opendaylight.protocol.bgp.parser.spi.pojo.ServiceLoaderBGPExtensionProviderContext;
 import org.opendaylight.protocol.bgp.rib.impl.BGPPeer;
 import org.opendaylight.protocol.bgp.rib.impl.RIBActivator;
 import org.opendaylight.protocol.bgp.rib.impl.RIBImpl;
 import org.opendaylight.protocol.bgp.rib.impl.spi.BGPDispatcher;
-import org.opendaylight.protocol.bgp.rib.impl.spi.BGPSessionPreferences;
+import org.opendaylight.protocol.bgp.rib.impl.spi.BGPPeerRegistry;
 import org.opendaylight.protocol.bgp.rib.mock.BGPMock;
 import org.opendaylight.protocol.bgp.rib.spi.AbstractRIBExtensionProviderActivator;
 import org.opendaylight.protocol.bgp.rib.spi.RIBExtensionProviderContext;
@@ -188,8 +183,8 @@ public class ParserToSalTest {
         }).when(this.mockedTransaction).readOperationalData(Matchers.any(InstanceIdentifier.class));
 
         Mockito.doReturn(GlobalEventExecutor.INSTANCE.newSucceededFuture(null)).when(this.dispatcher).createReconnectingClient(
-                Mockito.any(InetSocketAddress.class), Mockito.any(BGPSessionPreferences.class), Mockito.any(AsNumber.class),
-                Mockito.any(BGPSessionListener.class), Mockito.eq(this.tcpStrategyFactory), Mockito.eq(this.sessionStrategy),
+                Mockito.any(InetSocketAddress.class), Mockito.any(AsNumber.class),
+                Mockito.any(BGPPeerRegistry.class), Mockito.eq(this.tcpStrategyFactory), Mockito.eq(this.sessionStrategy),
                 Mockito.any(KeyMapping.class));
 
         this.ext = new SimpleRIBExtensionProviderContext();
@@ -208,7 +203,7 @@ public class ParserToSalTest {
 
     private void runTestWithTables(final List<BgpTableType> tables) {
         final RIBImpl rib = new RIBImpl(new RibId("testRib"), new AsNumber(72L), new Ipv4Address("127.0.0.1"), this.ext, this.dispatcher, this.tcpStrategyFactory, this.sessionStrategy, this.providerService, tables);
-        final BGPPeer peer = new BGPPeer("peer-" + this.mock.toString(), null, null, null, rib.getLocalAs(), rib);
+        final BGPPeer peer = new BGPPeer("peer-" + this.mock.toString(), rib);
 
         ListenerRegistration<?> reg = this.mock.registerUpdateListener(peer);
         reg.close();