import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelConfig;
import io.netty.channel.ChannelFuture;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import java.net.UnknownHostException;
import java.security.AccessControlException;
import org.opendaylight.controller.config.api.JmxAttributeValidationException;
+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.PeerRegistryListener;
+import org.opendaylight.tcpmd5.api.KeyMapping;
+import org.opendaylight.tcpmd5.netty.MD5ChannelOption;
+import org.opendaylight.tcpmd5.netty.MD5NioServerSocketChannel;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IetfInetUtil;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
/**
* BGP peer acceptor that handles incoming bgp connections.
}
}
+ private AutoCloseable listenerRegistration;
+
@Override
public java.lang.AutoCloseable createInstance() {
- final ChannelFuture future = getAcceptingBgpDispatcherDependency().createServer(getAcceptingPeerRegistryDependency(), getAddress());
+ final BGPPeerRegistry peerRegistry = getAcceptingPeerRegistryDependency();
+ final ChannelFuture futureChannel = getAcceptingBgpDispatcherDependency().createServer(peerRegistry, getAddress());
// Validate future success
- future.addListener(new GenericFutureListener<Future<Void>>() {
+ futureChannel.addListener(new GenericFutureListener<Future<Void>>() {
@Override
public void operationComplete(final Future<Void> future) {
Preconditions.checkArgument(future.isSuccess(), "Unable to start bgp server on %s", getAddress(), future.cause());
+ final Channel channel = futureChannel.channel();
+ if (channel instanceof MD5NioServerSocketChannel) {
+ BGPPeerAcceptorModule.this.listenerRegistration = peerRegistry.registerPeerRegisterListener(new PeerRegistryListenerImpl(channel.config()));
+ }
}
});
return new AutoCloseable() {
@Override
- public void close() {
+ 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();
+ futureChannel.cancel(true);
+ futureChannel.channel().close();
+ if (BGPPeerAcceptorModule.this.listenerRegistration != null) {
+ BGPPeerAcceptorModule.this.listenerRegistration.close();
+ }
}
};
}
return new InetSocketAddress(inetAddr, getBindingPort().getValue());
}
+ private static final class PeerRegistryListenerImpl implements PeerRegistryListener {
+
+ private final ChannelConfig channelConfig;
+
+ private final KeyMapping keys;
+
+ PeerRegistryListenerImpl(final ChannelConfig channelConfig) {
+ this.channelConfig = channelConfig;
+ this.keys = new KeyMapping();
+ }
+
+ @Override
+ public void onPeerAdded(final IpAddress ip, final BGPSessionPreferences prefs) {
+ if (prefs.getMd5Password().isPresent()) {
+ this.keys.put(IetfInetUtil.INSTANCE.inetAddressFor(ip), prefs.getMd5Password().get());
+ this.channelConfig.setOption(MD5ChannelOption.TCP_MD5SIG, this.keys);
+ }
+ }
+
+ @Override
+ public void onPeerRemoved(final IpAddress ip) {
+ if (this.keys.remove(IetfInetUtil.INSTANCE.inetAddressFor(ip)) != null) {
+ this.channelConfig.setOption(MD5ChannelOption.TCP_MD5SIG, this.keys);
+ }
+ }
+
+ }
+
}
final List<BgpParameters> tlvs = getTlvs(r);
final AsNumber remoteAs = getAsOrDefault(r);
- final BGPSessionPreferences prefs = new BGPSessionPreferences(r.getLocalAs(), getHoldtimer(), r.getBgpIdentifier(), remoteAs, tlvs);
+ final BGPSessionPreferences prefs = new BGPSessionPreferences(r.getLocalAs(), getHoldtimer(), r.getBgpIdentifier(), remoteAs, tlvs,
+ getMD5Password(getPassword()));
final BGPPeer bgpClientPeer;
final IpAddress host = getNormalizedHost();
if (getPeerRole() != null) {
return password != null && ! password.getValue().isEmpty() ? Optional.of(password) : Optional.<Rfc2385Key>absent();
}
+ private static Optional<byte[]> getMD5Password(final Rfc2385Key password) {
+ return password != null && ! password.getValue().isEmpty() ? Optional.of(password.getValue().getBytes(Charsets.US_ASCII)) : Optional.<byte[]>absent();
+ }
+
}
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.PeerRegistryListener;
import org.opendaylight.protocol.bgp.rib.spi.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;
.add("peers", this.global)
.toString();
}
+
+ @Override
+ public AutoCloseable registerPeerRegisterListener(final PeerRegistryListener listener) {
+ return this.global.registerPeerRegisterListener(listener);
+ }
}
}
private final BGPHandlerFactory handlerFactory;
private final EventLoopGroup bossGroup;
private final EventLoopGroup workerGroup;
- private Optional<KeyMapping> keys;
public BGPDispatcherImpl(final MessageRegistry messageRegistry, final EventLoopGroup bossGroup, final EventLoopGroup workerGroup) {
this(messageRegistry, bossGroup, workerGroup, null, null);
this.handlerFactory = new BGPHandlerFactory(messageRegistry);
this.channelFactory = cf;
this.serverChannelFactory = scf;
- this.keys = Optional.absent();
}
@Override
final BGPClientSessionNegotiatorFactory snf = new BGPClientSessionNegotiatorFactory(listener);
final ChannelPipelineInitializer initializer = BGPChannel.createChannelPipelineInitializer(BGPDispatcherImpl.this.handlerFactory, snf);
- final Bootstrap bootstrap = createClientBootStrap();
+ final Bootstrap bootstrap = createClientBootStrap(Optional.<KeyMapping>absent());
final BGPProtocolSessionPromise sessionPromise = new BGPProtocolSessionPromise(address, strategy, bootstrap);
bootstrap.handler(BGPChannel.createClientChannelHandler(initializer, sessionPromise));
sessionPromise.connect();
return sessionPromise;
}
- protected Bootstrap createClientBootStrap() {
+ protected Bootstrap createClientBootStrap(final Optional<KeyMapping> keys) {
final Bootstrap bootstrap = new Bootstrap();
- if (this.keys.isPresent()) {
+ if (keys.isPresent()) {
if (this.channelFactory == null) {
throw new UnsupportedOperationException("No key access instance available, cannot use key mapping");
}
bootstrap.channelFactory(this.channelFactory);
- bootstrap.option(MD5ChannelOption.TCP_MD5SIG, this.keys.get());
+ bootstrap.option(MD5ChannelOption.TCP_MD5SIG, keys.get());
} else {
bootstrap.channel(NioSocketChannel.class);
}
public synchronized Future<Void> createReconnectingClient(final InetSocketAddress address, final BGPPeerRegistry peerRegistry,
final ReconnectStrategyFactory connectStrategyFactory, final Optional<KeyMapping> keys) {
final BGPClientSessionNegotiatorFactory snf = new BGPClientSessionNegotiatorFactory(peerRegistry);
- this.keys = keys;
- final Bootstrap bootstrap = createClientBootStrap();
+ final Bootstrap bootstrap = createClientBootStrap(keys);
final BGPReconnectPromise reconnectPromise = new BGPReconnectPromise(GlobalEventExecutor.INSTANCE, address,
connectStrategyFactory, bootstrap, BGPChannel.createChannelPipelineInitializer(BGPDispatcherImpl.this.handlerFactory, snf));
reconnectPromise.connect();
- this.keys = Optional.absent();
return reconnectPromise;
}
private ServerBootstrap createServerBootstrap(final ChannelPipelineInitializer initializer) {
final ServerBootstrap serverBootstrap = new ServerBootstrap();
+ final boolean md5Supported = this.serverChannelFactory != null;
final ChannelHandler serverChannelHandler = BGPChannel.createServerChannelHandler(initializer);
serverBootstrap.childHandler(serverChannelHandler);
serverBootstrap.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
serverBootstrap.childOption(ChannelOption.WRITE_BUFFER_HIGH_WATER_MARK, HIGH_WATER_MARK);
serverBootstrap.childOption(ChannelOption.WRITE_BUFFER_LOW_WATER_MARK, LOW_WATER_MARK);
- if (this.keys.isPresent()) {
- if (this.serverChannelFactory == null) {
- throw new UnsupportedOperationException("No key access instance available, cannot use key mapping");
- }
+ if (md5Supported) {
serverBootstrap.channelFactory(this.serverChannelFactory);
- serverBootstrap.option(MD5ChannelOption.TCP_MD5SIG, this.keys.get());
} else {
serverBootstrap.channel(NioServerSocketChannel.class);
}
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;
import org.opendaylight.protocol.bgp.parser.AsNumberUtil;
import org.opendaylight.protocol.bgp.parser.impl.message.open.As4CapabilityHandler;
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.PeerRegistryListener;
import org.opendaylight.protocol.bgp.rib.spi.BGPSessionListener;
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.IetfInetUtil;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.open.message.bgp.parameters.optional.capabilities.CParameters;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.open.message.bgp.parameters.optional.capabilities.CParametersBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.open.message.bgp.parameters.optional.capabilities.c.parameters.As4BytesCapability;
+import org.opendaylight.yangtools.concepts.AbstractRegistration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private final Map<IpAddress, BGPSessionId> sessionIds = Maps.newHashMap();
@GuardedBy("this")
private final Map<IpAddress, BGPSessionPreferences> peerPreferences = Maps.newHashMap();
+ @GuardedBy("this")
+ private final Set<PeerRegistryListener> listeners = new HashSet<>();
@Override
public synchronized void addPeer(final IpAddress ip, final BGPSessionListener peer, final BGPSessionPreferences preferences) {
Preconditions.checkNotNull(preferences.getParams());
Preconditions.checkNotNull(preferences.getBgpId());
this.peerPreferences.put(ip, preferences);
+ for (final PeerRegistryListener peerRegistryListener : this.listeners) {
+ peerRegistryListener.onPeerAdded(ip, preferences);
+ }
}
@Override
public synchronized void removePeer(final IpAddress ip) {
Preconditions.checkNotNull(ip);
this.peers.remove(ip);
+ for (final PeerRegistryListener peerRegistryListener : this.listeners) {
+ peerRegistryListener.onPeerRemoved(ip);
+ }
}
@Override
.toString();
}
}
+
+ @Override
+ public synchronized AutoCloseable registerPeerRegisterListener(final PeerRegistryListener listener) {
+ this.listeners.add(listener);
+ for (final Entry<IpAddress, BGPSessionPreferences> entry : this.peerPreferences.entrySet()) {
+ listener.onPeerAdded(entry.getKey(), entry.getValue());
+ }
+ return new AbstractRegistration() {
+ @Override
+ protected void removeRegistration() {
+ synchronized (StrictBGPPeerRegistry.this) {
+ StrictBGPPeerRegistry.this.listeners.remove(listener);
+ }
+ }
+ };
+ }
}
\ No newline at end of file
package org.opendaylight.protocol.bgp.rib.impl.spi;
+import javax.annotation.Nonnull;
import org.opendaylight.protocol.bgp.parser.BGPDocumentedException;
import org.opendaylight.protocol.bgp.rib.spi.BGPSessionListener;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
*/
BGPSessionPreferences getPeerPreferences(IpAddress ip);
+ /**
+ * Register PeerRegistryListener, which listens to the changes in peer
+ * registry (add peer, remove peer). After registration, an initial
+ * drop is provided by calling onPeerAdded().
+ *
+ * @param listener The PeerRegistryListener to be registered.
+ * @return Registration ticked, used for closing of registration.
+ */
+ @Nonnull AutoCloseable registerPeerRegisterListener(@Nonnull PeerRegistryListener listener);
+
}
*/
package org.opendaylight.protocol.bgp.rib.impl.spi;
+import com.google.common.base.Optional;
import java.util.List;
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;
private final AsNumber remoteAs;
+ private final Optional<byte[]> md5Password;
+
/**
* Creates a new DTO for Open message.
*
* @param params list of advertised parameters
*/
public BGPSessionPreferences(final AsNumber as, final int hold, final Ipv4Address bgpId, final AsNumber remoteAs,
- final List<BgpParameters> params) {
+ final List<BgpParameters> params, final Optional<byte[]> md5Password) {
this.as = as;
this.hold = hold;
this.bgpId = bgpId;
this.remoteAs = remoteAs;
this.params = params;
+ this.md5Password = md5Password;
}
/**
public List<BgpParameters> getParams() {
return this.params;
}
+
+ /**
+ * Optionally returns peer's MD5 password.
+ * @return Encoded MD5 password.
+ */
+ public Optional<byte[]> getMd5Password() {
+ return this.md5Password;
+ }
}
--- /dev/null
+/*
+ * Copyright (c) 2016 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 javax.annotation.Nonnull;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
+
+/**
+ * Listens to the changes in a PeerRegisty.
+ *
+ */
+public interface PeerRegistryListener {
+
+ /**
+ * Invoked when new peer is added into the registry.
+ * @param ip The new peer's IP address.
+ * @param prefs The new peer's preferences.
+ */
+ void onPeerAdded(@Nonnull IpAddress ip, @Nonnull BGPSessionPreferences prefs);
+
+ /**
+ * Invoked when peer is removed from registry.
+ * @param ip The removed peer's IP address.
+ */
+ void onPeerRemoved(@Nonnull IpAddress ip);
+
+}
.setAfi(this.ipv4tt.getAfi()).setSafi(this.ipv4tt.getSafi()).build()).build())
.setAs4BytesCapability(new As4BytesCapabilityBuilder().setAsNumber(new AsNumber(30L)).build()).build()).build());
tlvs.add(new BgpParametersBuilder().setOptionalCapabilities(capas).build());
- return new BGPSessionPreferences(AS_NUMBER, (short) 4, new Ipv4Address(socketAddress.getAddress().getHostAddress()), AS_NUMBER, tlvs);
+ return new BGPSessionPreferences(AS_NUMBER, (short) 4, new Ipv4Address(socketAddress.getAddress().getHostAddress()), AS_NUMBER, tlvs,
+ Optional.<byte[]>absent());
}
}
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+
+import com.google.common.base.Optional;
import com.google.common.collect.Lists;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
tlvs.add(new BgpParametersBuilder().setOptionalCapabilities(capas).build());
- final BGPSessionPreferences prefs = new BGPSessionPreferences(new AsNumber(30L), (short) 3, new Ipv4Address("1.1.1.1"), new AsNumber(30L), tlvs);
+ final BGPSessionPreferences prefs = new BGPSessionPreferences(new AsNumber(30L), (short) 3, new Ipv4Address("1.1.1.1"), new AsNumber(30L), tlvs,
+ Optional.<byte[]>absent());
final ChannelFuture f = mock(ChannelFuture.class);
doReturn(null).when(f).addListener(any(GenericFutureListener.class));
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.fail;
+
+import com.google.common.base.Optional;
import com.google.common.collect.Lists;
import java.net.InetSocketAddress;
import java.util.Collections;
@Before
public void setUp() throws Exception {
this.peerRegistry = new StrictBGPPeerRegistry();
- this.mockPreferences = new BGPSessionPreferences(LOCAL_AS, 1, new Ipv4Address("0.0.0.1"), LOCAL_AS, Collections.<BgpParameters> emptyList());
+ this.mockPreferences = new BGPSessionPreferences(LOCAL_AS, 1, new Ipv4Address("0.0.0.1"), LOCAL_AS, Collections.<BgpParameters> emptyList(),
+ Optional.<byte[]>absent());
}
private static BGPSessionListener getMockSession() {
import org.opendaylight.protocol.bgp.rib.impl.spi.BGPPeerRegistry;
import org.opendaylight.protocol.framework.ReconnectStrategy;
import org.opendaylight.protocol.framework.ReconnectStrategyFactory;
+import org.opendaylight.tcpmd5.api.KeyMapping;
public class TestClientDispatcher {
final InetSocketAddress locaAddress) {
this.disp = new BGPDispatcherImpl(messageRegistry, bossGroup, workerGroup) {
@Override
- protected Bootstrap createClientBootStrap() {
+ protected Bootstrap createClientBootStrap(final Optional<KeyMapping> keys) {
final Bootstrap bootstrap = new Bootstrap();
bootstrap.channel(NioSocketChannel.class);
// Make sure we are doing round-robin processing
package org.opendaylight.protocol.bgp.testtool;
import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Optional;
import com.google.common.collect.Lists;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.util.concurrent.GlobalEventExecutor;
createMPCapability(LinkstateAddressFamily.class, LinkstateSubsequentAddressFamily.class)));
final BGPSessionPreferences proposal = new BGPSessionPreferences(as, holdTimerValue, new Ipv4Address("25.25.25.2"), as,
- Collections.singletonList(bgpParameters));
+ Collections.singletonList(bgpParameters), Optional.<byte[]>absent());
LOG.debug("{} {} {}", address, sessionListener, proposal);
*/
package org.opendaylight.protocol.bgp.testtool;
+import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import io.netty.channel.nio.NioEventLoopGroup;
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.PeerRegistryListener;
import org.opendaylight.protocol.bgp.rib.spi.BGPSessionListener;
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;
final BgpParameters bgpParameters = Main.createBgpParameters(Lists.newArrayList(
Main.createMPCapability(Ipv4AddressFamily.class, UnicastSubsequentAddressFamily.class),
Main.createMPCapability(LinkstateAddressFamily.class, LinkstateSubsequentAddressFamily.class)));
- return new BGPSessionPreferences(new AsNumber(72L), (short) 90, new Ipv4Address("127.0.0.2"), new AsNumber(72L), Collections.singletonList(bgpParameters));
+ return new BGPSessionPreferences(new AsNumber(72L), (short) 90, new Ipv4Address("127.0.0.2"), new AsNumber(72L),
+ Collections.singletonList(bgpParameters), Optional.<byte[]>absent());
}
@Override
@Override
public void removePeerSession(final IpAddress ip) {
}
+
+ @Override
+ public AutoCloseable registerPeerRegisterListener(final PeerRegistryListener listener) {
+ return null;
+ }
};
}