From: Robert Varga Date: Tue, 29 Apr 2014 12:43:02 +0000 (+0200) Subject: BUG-635: implement MD5 auth option for BGP peers X-Git-Tag: release/helium~312 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=commitdiff_plain;h=b40afa455f9d1898c8a1ed07b05220c5a3d68d31;p=bgpcep.git BUG-635: implement MD5 auth option for BGP peers This adds the wiring for the BGP dispatcher to make use of client/server channels. It also adds validation support. Change-Id: Ieb2dc4c60dfcd92d8e7a3f732198ad07a734853e Signed-off-by: Robert Varga --- diff --git a/bgp/pom.xml b/bgp/pom.xml index e8caf67de8..31ca786a77 100644 --- a/bgp/pom.xml +++ b/bgp/pom.xml @@ -176,6 +176,26 @@ topology-api-config ${project.version} + + ${project.groupId} + tcpmd5-api + ${project.version} + + + ${project.groupId} + tcpmd5-api-cfg + ${project.version} + + + ${project.groupId} + tcpmd5-netty + ${project.version} + + + ${project.groupId} + tcpmd5-netty-cfg + ${project.version} + diff --git a/bgp/rib-impl-config/pom.xml b/bgp/rib-impl-config/pom.xml index 41fc03b045..5e8e8915ee 100644 --- a/bgp/rib-impl-config/pom.xml +++ b/bgp/rib-impl-config/pom.xml @@ -64,6 +64,18 @@ ${project.groupId} bgp-parser-impl + + ${project.groupId} + tcpmd5-api-cfg + + + ${project.groupId} + tcpmd5-netty + + + ${project.groupId} + tcpmd5-netty-cfg + org.opendaylight.controller config-api diff --git a/bgp/rib-impl-config/src/main/java/org/opendaylight/controller/config/yang/bgp/rib/impl/BGPDispatcherImplModule.java b/bgp/rib-impl-config/src/main/java/org/opendaylight/controller/config/yang/bgp/rib/impl/BGPDispatcherImplModule.java index 20ee2623ca..cde62da8e8 100644 --- a/bgp/rib-impl-config/src/main/java/org/opendaylight/controller/config/yang/bgp/rib/impl/BGPDispatcherImplModule.java +++ b/bgp/rib-impl-config/src/main/java/org/opendaylight/controller/config/yang/bgp/rib/impl/BGPDispatcherImplModule.java @@ -44,6 +44,6 @@ org.opendaylight.controller.config.yang.bgp.rib.impl.AbstractBGPDispatcherImplMo public java.lang.AutoCloseable createInstance() { final BGPExtensionConsumerContext bgpExtensions = getBgpExtensionsDependency(); return new BGPDispatcherImpl(bgpExtensions.getMessageRegistry(), getTimerDependency(), - getBossGroupDependency(), getWorkerGroupDependency()); + getBossGroupDependency(), getWorkerGroupDependency(), getMd5ChannelFactoryDependency(), getMd5ServerChannelFactoryDependency()); } } diff --git a/bgp/rib-impl-config/src/main/java/org/opendaylight/controller/config/yang/bgp/rib/impl/BGPPeerModule.java b/bgp/rib-impl-config/src/main/java/org/opendaylight/controller/config/yang/bgp/rib/impl/BGPPeerModule.java index c983e04eaa..21ad7acddc 100644 --- a/bgp/rib-impl-config/src/main/java/org/opendaylight/controller/config/yang/bgp/rib/impl/BGPPeerModule.java +++ b/bgp/rib-impl-config/src/main/java/org/opendaylight/controller/config/yang/bgp/rib/impl/BGPPeerModule.java @@ -16,9 +16,17 @@ */ package org.opendaylight.controller.config.yang.bgp.rib.impl; +import java.lang.management.ManagementFactory; import java.net.InetSocketAddress; import java.util.List; +import javax.management.AttributeNotFoundException; +import javax.management.InstanceNotFoundException; +import javax.management.MBeanException; +import javax.management.MBeanServer; +import javax.management.ObjectName; +import javax.management.ReflectionException; + import org.opendaylight.controller.config.api.JmxAttributeValidationException; import org.opendaylight.protocol.bgp.rib.impl.BGPPeer; import org.opendaylight.protocol.bgp.rib.impl.spi.BGPSessionPreferences; @@ -61,6 +69,29 @@ public final class BGPPeerModule extends org.opendaylight.controller.config.yang "value is not set.", hostJmxAttribute); JmxAttributeValidationException.checkNotNull(getPort(), "value is not set.", portJmxAttribute); + + if (getPassword() != null) { + /* + * This is a nasty hack, but we don't have another clean solution. We cannot allow + * password being set if the injected dispatcher does not have the optional + * md5-server-channel-factory set. + * + * FIXME: this is a use case for Module interfaces, e.g. RibImplModule + * should something like isMd5ServerSupported() + */ + final MBeanServer srv = ManagementFactory.getPlatformMBeanServer(); + try { + // FIXME: AbstractRIBImplModule.bgpDispatcherJmxAttribute.getAttributeName() + final ObjectName disp = (ObjectName) srv.getAttribute(getRib(), "BgpDispatcher"); + + // FIXME: AbstractBGPDispatcherImplModule.md5ChannelFactoryJmxAttribute.getAttributeName() + final Object cf = srv.getAttribute(disp, "Md5ChannelFactory"); + JmxAttributeValidationException.checkCondition(cf != null, "Underlying dispatcher does not support MD5 clients", this.passwordJmxAttribute); + } catch (AttributeNotFoundException | InstanceNotFoundException + | MBeanException | ReflectionException e) { + JmxAttributeValidationException.wrap(e, passwordJmxAttribute); + } + } } private InetSocketAddress createAddress() { @@ -111,7 +142,14 @@ public final class BGPPeerModule extends org.opendaylight.controller.config.yang remoteAs = r.getLocalAs(); } - return new BGPPeer(peerName(getHost()), createAddress(), + final String password; + if (getPassword() != null) { + password = getPassword().getValue(); + } else { + password = null; + } + + return new BGPPeer(peerName(getHost()), createAddress(), password, new BGPSessionPreferences(r.getLocalAs(), getHoldtimer(), r.getBgpIdentifier(), tlvs), remoteAs, r); } } diff --git a/bgp/rib-impl-config/src/main/yang/bgp-rib-impl.yang b/bgp/rib-impl-config/src/main/yang/bgp-rib-impl.yang index 2088596d8d..b0df01aae2 100644 --- a/bgp/rib-impl-config/src/main/yang/bgp-rib-impl.yang +++ b/bgp/rib-impl-config/src/main/yang/bgp-rib-impl.yang @@ -15,6 +15,8 @@ module bgp-rib-impl { import netty { prefix netty; revision-date 2013-11-19; } import config { prefix config; revision-date 2013-04-05; } import protocol-framework { prefix pf; revision-date 2014-03-13; } + import odl-tcpmd5-cfg { prefix tcpmd5; revision-date 2014-04-27; } + import odl-tcpmd5-netty-cfg { prefix tcpmd5n; revision-date 2014-04-27; } organization "Cisco Systems, Inc."; @@ -101,6 +103,24 @@ module bgp-rib-impl { } } } + + container md5-channel-factory { + uses config:service-ref { + refine type { + mandatory false; + config:required-identity tcpmd5n:md5-channel-factory; + } + } + } + + container md5-server-channel-factory { + uses config:service-ref { + refine type { + mandatory false; + config:required-identity tcpmd5n:md5-server-channel-factory; + } + } + } } } @@ -205,6 +225,11 @@ module bgp-rib-impl { type uint32; } + leaf password { + type tcpmd5:rfc2385-key; + description "RFC2385 shared secret"; + } + container rib { uses config:service-ref { refine type { diff --git a/bgp/rib-impl/pom.xml b/bgp/rib-impl/pom.xml index 995acf2488..f50c427e92 100644 --- a/bgp/rib-impl/pom.xml +++ b/bgp/rib-impl/pom.xml @@ -64,6 +64,14 @@ ${project.groupId} bgp-util + + ${project.groupId} + tcpmd5-api + + + ${project.groupId} + tcpmd5-netty + com.google.code.findbugs jsr305 diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/BGPDispatcherImpl.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/BGPDispatcherImpl.java index e4bb409c3c..b6381a11d8 100644 --- a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/BGPDispatcherImpl.java +++ b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/BGPDispatcherImpl.java @@ -7,6 +7,8 @@ */ package org.opendaylight.protocol.bgp.rib.impl; +import io.netty.bootstrap.Bootstrap; +import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.EventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.util.Timer; @@ -15,6 +17,10 @@ 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.spi.BGPDispatcher; @@ -31,17 +37,26 @@ import com.google.common.base.Preconditions; * Implementation of BGPDispatcher. */ public final class BGPDispatcherImpl extends AbstractDispatcher implements BGPDispatcher, AutoCloseable { + private final MD5ServerChannelFactory scf; + private final MD5ChannelFactory cf; private final BGPHandlerFactory hf; private final Timer timer; + private KeyMapping keys; public BGPDispatcherImpl(final MessageRegistry messageRegistry, final Timer timer, final EventLoopGroup bossGroup, final EventLoopGroup workerGroup) { + this(messageRegistry, timer, bossGroup, workerGroup, null, null); + } + + public BGPDispatcherImpl(final MessageRegistry messageRegistry, final Timer timer, final EventLoopGroup bossGroup, final EventLoopGroup workerGroup, final MD5ChannelFactory cf, final MD5ServerChannelFactory scf) { super(bossGroup, workerGroup); this.timer = Preconditions.checkNotNull(timer); this.hf = new BGPHandlerFactory(messageRegistry); + this.cf = cf; + this.scf = scf; } @Override - public Future createClient(final InetSocketAddress address, final BGPSessionPreferences preferences, + public synchronized Future 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 slf = new SessionListenerFactory() { @@ -65,6 +80,19 @@ public final class BGPDispatcherImpl extends AbstractDispatcher createReconnectingClient(final InetSocketAddress address, + final BGPSessionPreferences preferences, final AsNumber remoteAs, + final BGPSessionListener listener, + final ReconnectStrategyFactory connectStrategyFactory, + final ReconnectStrategyFactory reestablishStrategyFactory, final KeyMapping keys) { final BGPSessionNegotiatorFactory snf = new BGPSessionNegotiatorFactory(this.timer, preferences, remoteAs); final SessionListenerFactory slf = new SessionListenerFactory() { @Override @@ -73,7 +101,8 @@ public final class BGPDispatcherImpl extends AbstractDispatcher() { + this.keys = keys; + final Future ret = super.createReconnectingClient(address, connectStrategyFactory, reestablishStrategyFactory.createReconnectStrategy(), new PipelineInitializer() { @Override public void initializeChannel(final SocketChannel ch, final Promise promise) { ch.pipeline().addLast(BGPDispatcherImpl.this.hf.getDecoders()); @@ -81,9 +110,32 @@ public final class BGPDispatcherImpl extends AbstractDispatcher cf; private BGPSession session; - public BGPPeer(final String name, final InetSocketAddress address, final BGPSessionPreferences prefs, + public BGPPeer(final String name, final InetSocketAddress address, final String password, final BGPSessionPreferences prefs, final AsNumber remoteAs, final RIB rib) { this.rib = Preconditions.checkNotNull(rib); this.name = Preconditions.checkNotNull(name); - this.cf = rib.getDispatcher().createReconnectingClient(address, prefs, remoteAs, this, rib.getTcpStrategyFactory(), rib.getSessionStrategyFactory()); + + 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); } @Override diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/spi/BGPDispatcher.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/spi/BGPDispatcher.java index 67d21d2bc0..320698878a 100644 --- a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/spi/BGPDispatcher.java +++ b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/spi/BGPDispatcher.java @@ -11,6 +11,7 @@ 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; @@ -24,7 +25,7 @@ public interface BGPDispatcher { /** * Creates BGP client. - * + * * @param address Peer address * @param preferences connection attributes required for connection * @param listener BGP message listener @@ -35,4 +36,7 @@ public interface BGPDispatcher { Future createReconnectingClient(InetSocketAddress address, BGPSessionPreferences preferences, AsNumber remoteAs, BGPSessionListener listener, ReconnectStrategyFactory connectStrategyFactory, ReconnectStrategyFactory reestablishStrategyFactory); + + Future createReconnectingClient(InetSocketAddress address, BGPSessionPreferences preferences, AsNumber remoteAs, + BGPSessionListener listener, ReconnectStrategyFactory connectStrategyFactory, ReconnectStrategyFactory reestablishStrategyFactory, KeyMapping keys); } diff --git a/integration-tests/src/test/java/org/opendaylight/protocol/integration/BgpRibImplBundleTest.java b/integration-tests/src/test/java/org/opendaylight/protocol/integration/BgpRibImplBundleTest.java index 4c5eb468bb..1e1b3b293f 100644 --- a/integration-tests/src/test/java/org/opendaylight/protocol/integration/BgpRibImplBundleTest.java +++ b/integration-tests/src/test/java/org/opendaylight/protocol/integration/BgpRibImplBundleTest.java @@ -16,7 +16,8 @@ public final class BgpRibImplBundleTest extends AbstractBundleTest { protected Collection prerequisiteBundles() { return Lists.newArrayList("concepts", "bgp-concepts", "bgp-linkstate", "bgp-parser-api", "bgp-parser-impl", "bgp-parser-spi", "bgp-rib-api", "bgp-rib-spi", - "bgp-util", "rsvp-api", "util"); + "bgp-util", "rsvp-api", "tcpmd5-api", "tcpmd5-jni", "tcpmd5-netty", + "tcpmd5-nio", "util"); } @Override diff --git a/integration-tests/src/test/java/org/opendaylight/protocol/integration/BgpRibMockBundleTest.java b/integration-tests/src/test/java/org/opendaylight/protocol/integration/BgpRibMockBundleTest.java index ecb7292525..ce3b0f79b1 100644 --- a/integration-tests/src/test/java/org/opendaylight/protocol/integration/BgpRibMockBundleTest.java +++ b/integration-tests/src/test/java/org/opendaylight/protocol/integration/BgpRibMockBundleTest.java @@ -16,7 +16,8 @@ public final class BgpRibMockBundleTest extends AbstractBundleTest { protected Collection prerequisiteBundles() { return Lists.newArrayList("concepts", "bgp-concepts", "bgp-linkstate", "bgp-parser-api", "bgp-parser-impl", "bgp-parser-spi", "bgp-rib-api", "bgp-rib-impl", - "bgp-rib-spi", "bgp-util", "rsvp-api", "util"); + "bgp-rib-spi", "bgp-util", "rsvp-api", "tcpmd5-api", "tcpmd5-jni", + "tcpmd5-nio", "tcpmd5-netty", "util"); } @Override diff --git a/integration-tests/src/test/java/org/opendaylight/protocol/integration/bgp/ParserToSalTest.java b/integration-tests/src/test/java/org/opendaylight/protocol/integration/bgp/ParserToSalTest.java index fde8121d85..87e4c2cf1b 100644 --- a/integration-tests/src/test/java/org/opendaylight/protocol/integration/bgp/ParserToSalTest.java +++ b/integration-tests/src/test/java/org/opendaylight/protocol/integration/bgp/ParserToSalTest.java @@ -29,6 +29,7 @@ import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; +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; @@ -159,7 +160,7 @@ public class ParserToSalTest { 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(BGPSessionListener.class), Mockito.eq(this.tcpStrategyFactory), Mockito.eq(this.sessionStrategy), Mockito.any(KeyMapping.class)); this.ext = new SimpleRIBExtensionProviderContext(); this.baseact = new RIBActivator(); @@ -178,7 +179,7 @@ public class ParserToSalTest { private void runTestWithTables(final List 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, rib.getLocalAs(), rib); + final BGPPeer peer = new BGPPeer("peer-" + this.mock.toString(), null, null, null, rib.getLocalAs(), rib); ListenerRegistration reg = this.mock.registerUpdateListener(peer); reg.close();