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>
<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>
</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>
*/
package org.opendaylight.protocol.bgp.parser;
+import com.google.common.base.Objects;
import org.opendaylight.protocol.framework.TerminationReason;
public final class BGPTerminationReason implements TerminationReason {
public String getErrorMessage() {
return error.toString();
}
+
+ @Override
+ public String toString() {
+ return Objects.toStringHelper(this)
+ .add("error", error)
+ .toString();
+ }
}
--- /dev/null
+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());
+ }
+
+}
--- /dev/null
+/*
+* 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 {
+
+}
*/
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;
}
}
- 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());
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;
+ }
+
}
--- /dev/null
+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();
+ }
+ }
+
+}
--- /dev/null
+/*
+* 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 {
+
+}
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.";
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";
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 {
}
}
}
+
+ 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;
+ }
+ }
+ }
}
}
--- /dev/null
+/*
+ * 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) {}
+ }
+}
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;
moduleFactories.add(new BGPTableTypeImplModuleFactory());
moduleFactories.add(new NativeKeyAccessFactoryModuleFactory());
moduleFactories.add(new MD5ClientChannelFactoryModuleFactory());
+ moduleFactories.add(new StrictBgpPeerRegistryModuleFactory());
return moduleFactories;
}
public void testCreateBean() throws Exception {
final CommitStatus status = createBgpPeerInstance();
assertBeanCount(1, FACTORY_NAME);
- assertStatus(status, 16, 0, 0);
+ assertStatus(status, 17, 0, 0);
}
@Test
NativeTestSupport.assumeSupportedPlatform();
final CommitStatus status = createBgpPeerInstance(true);
assertBeanCount(1, FACTORY_NAME);
- assertStatus(status, 18, 0, 0);
+ assertStatus(status, 19, 0, 0);
}
@Test
assertBeanCount(1, FACTORY_NAME);
status = transaction.commit();
assertBeanCount(1, FACTORY_NAME);
- assertStatus(status, 0, 0, 16);
+ assertStatus(status, 0, 0, 17);
}
@Test
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,
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));
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);
/*
- * 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;
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;
@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;
@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);
}
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;
}
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;
}
@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);
}
}
@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,
@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());
}
});
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()) {
/*
- * 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;
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
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());
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;
+ }
+ }
}
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);
--- /dev/null
+/*
+ * 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();
+ }
+ }
+}
--- /dev/null
+/*
+ * 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();
+ }
+}
* 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);
}
}
--- /dev/null
+/*
+ * 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);
+ }
+ }
+}
--- /dev/null
+/*
+ * 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();
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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
+ }
+}
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;
/**
* 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);
}
--- /dev/null
+/*
+ * 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);
+
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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();
+
+}
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;
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;
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;
public class FSMTest {
- private BGPSessionNegotiator clientSession;
+ private BGPClientSessionNegotiator clientSession;
@Mock
private Channel speakerListener;
private Open classicOpen;
@Before
- public void setUp() {
+ public void setUp() throws UnknownHostException {
MockitoAnnotations.initMocks(this);
final List<BgpParameters> tlvs = Lists.newArrayList();
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) {
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
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());
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()));
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);
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;
/**
* Listener for the client.
*/
-public class SimpleSessionListener implements BGPSessionListener {
+public class SimpleSessionListener implements ReusableBGPPeer {
private final List<Notification> listMsg = Lists.newArrayList();
public void onSessionTerminated(final BGPSession session, final BGPTerminationReason cause) {
LOG.debug("Session terminated. Cause : {}", cause.toString());
}
+
+ @Override
+ public void releaseConnection() {
+ LOG.debug("Releasing connection");
+ }
}
--- /dev/null
+/*
+ * 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);
+ }
+}
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;
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);
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));
}
}
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;
/**
* Testing BGP Listener.
*/
-public class TestingListener implements BGPSessionListener {
+public class TestingListener implements ReusableBGPPeer {
private static final Logger LOG = LoggerFactory.getLogger(TestingListener.class);
@Override
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.");
+ }
}
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;
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));
}
}
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;
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;
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;
}).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();
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();