<artifactId>topology-api-config</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>tcpmd5-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>tcpmd5-api-cfg</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>tcpmd5-netty</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>tcpmd5-netty-cfg</artifactId>
+ <version>${project.version}</version>
+ </dependency>
</dependencies>
</dependencyManagement>
<groupId>${project.groupId}</groupId>
<artifactId>bgp-parser-impl</artifactId>
</dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>tcpmd5-api-cfg</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>tcpmd5-netty</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>tcpmd5-netty-cfg</artifactId>
+ </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>config-api</artifactId>
public java.lang.AutoCloseable createInstance() {
final BGPExtensionConsumerContext bgpExtensions = getBgpExtensionsDependency();
return new BGPDispatcherImpl(bgpExtensions.getMessageRegistry(), getTimerDependency(),
- getBossGroupDependency(), getWorkerGroupDependency());
+ getBossGroupDependency(), getWorkerGroupDependency(), getMd5ChannelFactoryDependency(), getMd5ServerChannelFactoryDependency());
}
}
*/
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;
"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() {
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);
}
}
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.";
}
}
}
+
+ 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;
+ }
+ }
+ }
}
}
type uint32;
}
+ leaf password {
+ type tcpmd5:rfc2385-key;
+ description "RFC2385 shared secret";
+ }
+
container rib {
uses config:service-ref {
refine type {
<groupId>${project.groupId}</groupId>
<artifactId>bgp-util</artifactId>
</dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>tcpmd5-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>tcpmd5-netty</artifactId>
+ </dependency>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
*/
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;
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;
* Implementation of BGPDispatcher.
*/
public final class BGPDispatcherImpl extends AbstractDispatcher<BGPSessionImpl, BGPSessionListener> 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<BGPSessionImpl> createClient(final InetSocketAddress address, final BGPSessionPreferences preferences,
+ 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>() {
final BGPSessionPreferences preferences, final AsNumber remoteAs,
final BGPSessionListener listener, final ReconnectStrategyFactory connectStrategyFactory,
final ReconnectStrategyFactory reestablishStrategyFactory) {
+ return this.createReconnectingClient(address, preferences, remoteAs, listener, connectStrategyFactory, reestablishStrategyFactory, null);
+ }
+
+ @Override
+ public void close() {
+ }
+
+ @Override
+ public synchronized Future<Void> 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<BGPSessionListener> slf = new SessionListenerFactory<BGPSessionListener>() {
@Override
}
};
- return super.createReconnectingClient(address, connectStrategyFactory, reestablishStrategyFactory.createReconnectStrategy(), new PipelineInitializer<BGPSessionImpl>() {
+ this.keys = keys;
+ final Future<Void> ret = super.createReconnectingClient(address, connectStrategyFactory, reestablishStrategyFactory.createReconnectStrategy(), new PipelineInitializer<BGPSessionImpl>() {
@Override
public void initializeChannel(final SocketChannel ch, final Promise<BGPSessionImpl> promise) {
ch.pipeline().addLast(BGPDispatcherImpl.this.hf.getDecoders());
ch.pipeline().addLast(BGPDispatcherImpl.this.hf.getEncoders());
}
});
+ this.keys = null;
+
+ return ret;
}
+
@Override
- public void close() {
+ protected void customizeBootstrap(final Bootstrap b) {
+ if (keys != null && !keys.isEmpty()) {
+ if (cf == null) {
+ throw new UnsupportedOperationException("No key access instance available, cannot use key mapping");
+ }
+ b.channelFactory(cf);
+ b.option(MD5ChannelOption.TCP_MD5SIG, keys);
+ }
}
+
+ @Override
+ protected void customizeBootstrap(final ServerBootstrap b) {
+ if (keys != null && !keys.isEmpty()) {
+ if (scf == null) {
+ throw new UnsupportedOperationException("No key access instance available, cannot use key mapping");
+ }
+ b.channelFactory(scf);
+ b.option(MD5ChannelOption.TCP_MD5SIG, keys);
+ }
+ }
+
}
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
+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;
private Future<Void> 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
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;
/**
* Creates BGP client.
- *
+ *
* @param address Peer address
* @param preferences connection attributes required for connection
* @param listener BGP message listener
Future<Void> createReconnectingClient(InetSocketAddress address, BGPSessionPreferences preferences, AsNumber remoteAs,
BGPSessionListener listener, ReconnectStrategyFactory connectStrategyFactory, ReconnectStrategyFactory reestablishStrategyFactory);
+
+ Future<Void> createReconnectingClient(InetSocketAddress address, BGPSessionPreferences preferences, AsNumber remoteAs,
+ BGPSessionListener listener, ReconnectStrategyFactory connectStrategyFactory, ReconnectStrategyFactory reestablishStrategyFactory, KeyMapping keys);
}
protected Collection<String> 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
protected Collection<String> 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
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;
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();
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, 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();