0 准备说明
- 版本:openflowplugin-release-oxygen-sr2
- 功能:openflow服务器创建及连接对象流程;
1 启动分析
1.1 blueprint配置文件位置
OpenFlowPluginProviderImpl通过OpenFlowPluginProviderFactoryImpl创建,其在两个配置文件中都有启动,一个是
org/opendaylight/blueprint/openflowplugin.xml
另一个为:
org/opendaylight/blueprint/openflowplugin-impl.xml
多个配置文件都存在的情况下,具体在哪个启动还有待进一步分析和总结;
1.2 启动netty服务、客户端代码
org/opendaylight/openflowplugin/impl/OpenFlowPluginProviderImpl.java
启动后执行 onSystemBootReady() ---> startSwitchConnections
//轮询所有switchConnectionProviders执行
switchConnectionProvider.setSwitchConnectionHandler(connectionManager);
return switchConnectionProvider.startup();
---------------------------------------------
org/opendaylight/openflowjava/protocol/impl/core/SwitchConnectionProviderImpl.java
ListenableFuture<Boolean> startup();
---->
createAndConfigureServer()
----->
createAndConfigureServer方法根据ConnectionConfiguration启动配置生成不同 的ServerFacade,包括TcpHandler和UdpHandler,完成服务器的基本参数配置,特别是TcpChannelInitializer channel初始化器,同时获取到服务的工作线程组,用于客户端方式的主动连接配置
总结: org/opendaylight/openflowjava/protocol/impl/core/SwitchConnectionProviderImpl.java类中,OpenFlowPluginProviderImpl通过调用createAndConfigureServer完成了netty服务端的初始化以及客户端初始化。但是无论是服务端还是客户端,有两个参数是不变的:
- 一个是channel初始化器(含有序列化器、消息处理handler)
- 另外一个是公用工作线程组;
这两点保障了无论是客户端还是服务端方式,openflow协议的上层可以是等同的,只是传输层有变化,支持了传输层的多样化,也保持了应用层的一致性。
2 channel初始化器分析
重写的这个初始化器,含有我们所有需要关注的入口位置,有必要把函数列出来重点分析以下:
protected void initChannel(final SocketChannel ch) {
//流程1:检查远端ip,检查是否接受此ip地址,不接受则断开连接;
if (ch.remoteAddress() != null) {
final InetAddress switchAddress = ch.remoteAddress().getAddress();
final int port = ch.localAddress().getPort();
final int remotePort = ch.remoteAddress().getPort();
LOG.debug("Incoming connection from (remote address): {}:{} --> :{}",
switchAddress.toString(), remotePort, port);
if (!getSwitchConnectionHandler().accept(switchAddress)) {
ch.disconnect();
LOG.debug("Incoming connection rejected");
return;
}
}
LOG.debug("Incoming connection accepted - building pipeline");
//步骤2:保存通道;
allChannels.add(ch);
ConnectionFacade connectionFacade = null;
//步骤3:新建连接工具类
connectionFacade = connectionAdapterFactory.createConnectionFacade(ch, null, useBarrier(),
getChannelOutboundQueueSize());
try {
LOG.debug("Calling OF plugin: {}", getSwitchConnectionHandler());
//步骤4-getSwitchConnectionHandler基本参数填入适配器
getSwitchConnectionHandler().onSwitchConnected(connectionFacade);
connectionFacade.checkListeners();
ch.pipeline().addLast(PipelineHandlers.IDLE_HANDLER.name(),
new IdleHandler(getSwitchIdleTimeout(), TimeUnit.MILLISECONDS));
boolean tlsPresent = false;
// If this channel is configured to support SSL it will only support SSL
if (getTlsConfiguration() != null) {
tlsPresent = true;
final SslContextFactory sslFactory = new SslContextFactory(getTlsConfiguration());
final SSLEngine engine = sslFactory.getServerContext().createSSLEngine();
engine.setNeedClientAuth(true);
engine.setUseClientMode(false);
List<String> suitesList = getTlsConfiguration().getCipherSuites();
if (suitesList != null && !suitesList.isEmpty()) {
LOG.debug("Requested Cipher Suites are: {}", suitesList);
String[] suites = suitesList.toArray(new String[suitesList.size()]);
engine.setEnabledCipherSuites(suites);
LOG.debug("Cipher suites enabled in SSLEngine are: {}",
Arrays.toString(engine.getEnabledCipherSuites()));
}
final SslHandler ssl = new SslHandler(engine);
final Future<Channel> handshakeFuture = ssl.handshakeFuture();
final ConnectionFacade finalConnectionFacade = connectionFacade;
handshakeFuture.addListener(future -> finalConnectionFacade.fireConnectionReadyNotification());
ch.pipeline().addLast(PipelineHandlers.SSL_HANDLER.name(), ssl);
}
ch.pipeline().addLast(PipelineHandlers.OF_FRAME_DECODER.name(),
new OFFrameDecoder(connectionFacade, tlsPresent));
ch.pipeline().addLast(PipelineHandlers.OF_VERSION_DETECTOR.name(), new OFVersionDetector());
final OFDecoder ofDecoder = new OFDecoder();
ofDecoder.setDeserializationFactory(getDeserializationFactory());
ch.pipeline().addLast(PipelineHandlers.OF_DECODER.name(), ofDecoder);
final OFEncoder ofEncoder = new OFEncoder();
ofEncoder.setSerializationFactory(getSerializationFactory());
ch.pipeline().addLast(PipelineHandlers.OF_ENCODER.name(), ofEncoder);
ch.pipeline().addLast(PipelineHandlers.DELEGATING_INBOUND_HANDLER.name(),
new DelegatingInboundHandler(connectionFacade));
if (!tlsPresent) {
connectionFacade.fireConnectionReadyNotification();
}
} catch (RuntimeException e) {
LOG.warn("Failed to initialize channel", e);
ch.close();
}
}
2.1 连接适配器--connectionFacade
抽象父类为AbstractConnectionAdapter,含有通道的基本连接信息,抽象方法为几个重要监听器的设置,实现类ConnectionAdapterImpl中包含如下几个重要监听器和模块:
- ConnectionReadyListener–连接准备监听器
- OpenflowProtocolListener–协议监听器-》接收后续的各种协议消息,完成消息处理;
- SystemNotificationsListener–系统订阅监听器,主要监听交换机闲置和断连事件;
- AlienMessageListener–不同性质消息监听器;监听不识别的消息???待后续进一步分析确定功能;
- SystemNotificationsListener–输出管理队列
- OFVersionDetector–版本检测器
- useBarrier–是否使用useBarrier标志;
2.2 配器参数提供者--SwitchConnectionHandler
在2章开头代码的第四步,如下代码为生成的适配器提供了基本的连接监听器等参数的初始设置;
getSwitchConnectionHandler().onSwitchConnected(connectionFacade);
onSwitchConnected的核心功能就是new出2.1提到的几个重要监听器,然后连接成功后把相关参数传入connectionFacade;
- ConnectionReadyListenerImpl
- connectionContext ----> ConnectionContextImpl
- handshakeContext ----> HandshakeContextImpl
- executorService
- handshakeManager ----> createHandshakeManager(connectionAdapter, handshakeListener);
- OpenflowProtocolListenerInitialImpl
- SystemNotificationsListenerImpl
2.3 消息内部代理类--DelegatingInboundHandler
此类负责注册,以及监听到消息后,调度消息消费者,完成消息的监听器执行,进入不同的消息处理流程;
ch.pipeline().addLast(PipelineHandlers.DELEGATING_INBOUND_HANDLER.name(),
new DelegatingInboundHandler(connectionFacade));
此处代码完成openflow消息的直接处理注册,当监听到消息,由这里的DelegatingInboundHandler调用channelRead,完成消息的读操作;
DelegatingInboundHandler ---> channelRead
---->consumer.consume((DataObject) msg);
这里的consumer则是上述2.1和2.2生产构造的connectionFacade;
---->
consumeDeviceMessage
- ConnectionAdapterImpl重写了consumeDeviceMessage消息完成消息的处理和调用不同的监听器,后续查看类似代码可以通过适配器的consumeDeviceMessage为入口继续分析相关流程;
3 openflow连接建立流程
3.1 收到hello消息-OpenflowProtocolListenerInitialImpl
- 2.2一开始注册的OpenflowProtocolListener为OpenflowProtocolListenerInitialImpl,也就是初始化消息相关的一些处理;一旦连接成功后,后续会替换掉该listener换成OpenflowProtocolListenerFullImpl,后面步骤会分析到此处;
主要函数执行位置为OpenflowProtocolListenerInitialImpl--》onHelloMessage;
此时通道的状态应该为CONNECTION_STATE.HANDSHAKING,函数主要功能是开启一个HandshakeStepWrapper,启动线程进行握手处理;
-
HandshakeManagerImpl的握手处理如下所示:
//01-如果未收到hello消息,先发送hello