控制器與交換機建立連接配接的過程主要分為三個階段:控制器啟動監聽6633端口、交換機與控制器建立連接配接、控制器與交換機版本協商。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICO5czN1gDM3EjNyQDM1EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
控制器啟動監聽6633端口
控制器啟動的入口函數為
@Activate
OpenFlowControllerImpl.activate(Component context)
OSGI子產品啟動時候,就會調用這個函數,其中Activate注解說明了這一點:
The Activate annotation defines the method which is used to activate the component.
在OpenFlowControllerImpl類初始化的過程中,首先會執行個體化Controller和内部類OpenFlowSwitchAgent,OpenFlowSwitchAgent類十分重要,它用于跟蹤已經建立連接配接的交換機以及它處于的狀态。
private final Controller ctrl = new Controller();
protected OpenFlowSwitchAgent agent = new OpenFlowSwitchAgent();
然後啟動控制器Controller.start(OpenFlowAgent agent),主要是配置參數,啟動server端,監聽端口,等待交換機建立連接配接。
public void run() {
try {
final ServerBootstrap bootstrap = createServerBootStrap();
bootstrap.setOption("reuseAddr", true);
bootstrap.setOption("child.keepAlive", true);
bootstrap.setOption("child.tcpNoDelay", true);
bootstrap.setOption("child.sendBufferSize", Controller.SEND_BUFFER_SIZE);
ChannelPipelineFactory pfact =
new OpenflowPipelineFactory(this, null);
bootstrap.setPipelineFactory(pfact);
InetSocketAddress sa = new InetSocketAddress(openFlowPort);
cg = new DefaultChannelGroup();
cg.add(bootstrap.bind(sa));
log.info("Listening for switch connections on {}", sa);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
其中cg.add(bootstrap.bind(sa));表示控制器已經啟動,并且開始監聽相應的端口。代碼看到這裡,需要有netty 的背景知識,下面将着重分析交換機連接配接控制器的代碼。
交換機與控制器建立連接配接
上一段代碼中比較重要的是
ChannelPipelineFactory pfact =
new OpenflowPipelineFactory(this, null);
這裡将pipeline初始化為OpenFlowPipelineFactory,并且在OpenFlowPipelineFactory中重寫ChannelPipelineFactory接口的getPipeline方法。
public OpenflowPipelineFactory(Controller controller,
ThreadPoolExecutor pipelineExecutor) {
super();
this.controller = controller;
this.pipelineExecutor = pipelineExecutor;
this.timer = new HashedWheelTimer();
this.idleHandler = new IdleStateHandler(timer, 20, 25, 0);
this.readTimeoutHandler = new ReadTimeoutHandler(timer, 30);
}
@Override
public ChannelPipeline getPipeline() throws Exception {
OFChannelHandler handler = new OFChannelHandler(controller);
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("ofmessagedecoder", new OFMessageDecoder());
pipeline.addLast("ofmessageencoder", new OFMessageEncoder());
pipeline.addLast("idle", idleHandler);
pipeline.addLast("timeout", readTimeoutHandler);
// XXX S ONOS: was 15 increased it to fix Issue #296
pipeline.addLast("handshaketimeout",
new HandshakeTimeoutHandler(handler, timer, 60));
if (pipelineExecutor != null) {
pipeline.addLast("pipelineExecutor",
new ExecutionHandler(pipelineExecutor));
}
pipeline.addLast("handler", handler);
return pipeline;
}
OpenflowPipelineFactory.getPipeline()方法作用:
為一個新的Channel建立一個新的ChannelPipeline,當一個server端的Channel接收到一個新的連接配接,我們會為每個新
的接受了的連接配接建立一個新的子Channel。這個新的子Channel使用一個新的ChannelPipeline,這個新的ChannelPipeline
由server端ChannelPipelineFactory的getPipeline建立。
在ChannelPipeline中,有idleHandler、readTimeoutHandler、HandshakeTimeoutHandler和handler這四個handler,其中
前三個handler用于逾時處理,而第四個handler用于捕獲這些逾時産生的idleStateEvent(OFChannelHandler.channelIdle:
發送EchoRequest消息,OF心跳包)、readTimeoutException(OFChannelHandler.exceptionCaught:記錄日志,關閉連接配接)、
HandshakeException(OFChannelHandler.exceptionCaught:記錄日志,關閉連接配接)。
接下來分析這幾個逾時的設定:在初始化idleHandler的參數,this.idleHandler = new IdleStateHandler(timer, 20, 25, 0); 其中20表示讀逾時,意味着如果20秒内沒有讀到用戶端發送的OF消息,則發送idleStateEvent給後面的handler處理,handler收到idleStateEvent之後,發送EchoRequest消息,這個消息是OF協定的心跳包。25表示如果25秒内沒有發送OF消息給用戶端,則觸發同樣的操作。this.readTimeoutHandler = new ReadTimeoutHandler(timer, 30);其中30表示如果30秒内沒有讀到OF消息,則抛出一個readTimeoutException給後面的handler處理,處理方法是斷開連接配接。HandshakeTimeoutHandler(handler, timer, 60),其中60意味着60需要完成握手過程,否則也是斷開連接配接。由20和30這兩個數字表明:如果20秒内沒有收到心跳包,則控制器發送一個心跳包,交換機傳回一個心跳包,控制器必須在30秒内必須收到回包,是以這意味着一個心跳周期最多不能超過10秒。
OFChannelHandler中主要關注複寫SimpleChannelHandler類的channelConnected和messageReceived這兩個方法,其中channelConnected用于處理Channel建立連接配接的ChannelStateEvent,messageReceived用于處理Channel接收到的MessageEvent。
@Override
public void channelConnected(ChannelHandlerContext ctx,
ChannelStateEvent e) throws Exception {
channel = e.getChannel();
log.info("New switch connection from {}",
channel.getRemoteAddress());
/*
hack to wait for the switch to tell us what it's
max version is. This is not spec compliant and should
be removed as soon as switches behave better.
*/
//sendHandshakeHelloMessage();
setState(ChannelState.WAIT_HELLO);
}
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
throws Exception {
if (e.getMessage() instanceof List) {
@SuppressWarnings("unchecked")
List<OFMessage> msglist = (List<OFMessage>) e.getMessage();
for (OFMessage ofm : msglist) {
// Do the actual packet processing
state.processOFMessage(this, ofm);
}
} else {
state.processOFMessage(this, (OFMessage) e.getMessage());
}
}
在channelConnected方法中,控制器将自身的ChannelState設定為WAIT_HELLO,意為等待交換機發送hello消息,這種代碼設計模式為狀态機模式。下一篇部落格将詳細分析ChannelState的狀态機運轉。接着在messageReceived方法中,對每個接收到的OF消息進行處理。到這裡,交換機與控制器已經建立了TCP連接配接,接下來,将分析控制器和交換機握手的過程,即版本協商。
控制器與交換機進行版本協商
在ChannelState為WAIT_HELLO狀态下,控制器一旦收到OF Hello的消息,便會調用processOFHello方法,在這個方法中,進行版本協商,如果協商失敗,控制器則會主動斷開連接配接。從代碼中可以看出:
- 控制器目前隻支援OF10和OF13協定
- 檢查版本bitmap來進行的代碼還沒開發
-
@Override void processOFHello(OFChannelHandler h, OFHello m) throws IOException { // TODO We could check for the optional bitmap, but for now // we are just checking the version number. if (m.getVersion() == OFVersion.OF_13) { log.debug("Received {} Hello from {}", m.getVersion(), h.channel.getRemoteAddress()); h.sendHandshakeHelloMessage(); h.ofVersion = OFVersion.OF_13; } else if (m.getVersion() == OFVersion.OF_10) { log.debug("Received {} Hello from {} - switching to OF " + "version 1.0", m.getVersion(), h.channel.getRemoteAddress()); OFHello hi = h.factory10.buildHello() .setXid(h.handshakeTransactionIds--) .build(); h.channel.write(Collections.singletonList(hi)); h.ofVersion = OFVersion.OF_10; } else { log.error("Received Hello of version {} from switch at {}. " + "This controller works with OF1.0 and OF1.3 " + "switches. Disconnecting switch ...", m.getVersion(), h.channel.getRemoteAddress()); h.channel.disconnect(); return; } h.sendHandshakeFeaturesRequestMessage(); h.setState(WAIT_FEATURES_REPLY); }