Dabai的个人博客

ONOS中的OpenFlow Channel状态机解析

ONOS使用OFChannelHandler类来处理交换机和控制器的信道连接,同时将switch message分发到合适的位置,代码在package org.onosproject.openflow.controller.impl中。

1. OpenFlow状态机:ChanelState

在OFChannelHandler类中,最重要的就是一个ChanelState的枚举类了,枚举类中的各个枚举实例表示Channel所处的状态,用来描述OpenFlow状态机信息,在枚举类中添加了处理OpenFlow消息的方法,各个枚举实例也根据自身的状态重写了部分方法。

OpenFlow状态机共定义了8种状态(INIT, WAIT_HELLO, WAIT_FEATURES_REPLY, WAIT_PORT_DESC_REPLY, WAIT_CONFIG_REPLY, WAIT_DESCRIPTION_STAT_REPLY, WAIT_SWITCH_DRIVER_SUB_HANDSHAKE, ACTIVE),任何时候,Channel处于状态机中的任意一种状态。每个状态(枚举实例)都有一个默认初始化参数,其中false表示交换机握手没有成功,true表示交换机握手成功,枚举类的构造函数和获取交换机握手状态的代码块如下:

1
2
3
4
5
6
7
8
9
10
11
12
private final boolean handshakeComplete;
ChannelState(boolean handshakeComplete) {
this.handshakeComplete = handshakeComplete;
}

/**
* Is this a state in which the handshake has completed?
* @return true if the handshake is complete
*/
public boolean isHandshakeComplete() {
return handshakeComplete;
}

2. OpenFlow状态机启动流程

ONOS使用netty通信框架(参考:Netty 实战),在这里就不考虑netty相关的NIO过程了。ONOS启动ChannelHander对应的实例时,构造函数OFChannelHandler(Controller controller) 会引用当前的控制器实例,同时会将stage初始化为ChannelState.INIT状态,表示Channel处于连接之前(代码注释很清楚)。注意,OFChannelHandler中的state变量是volatile类型,这是为了确保HandshakeTimeoutHandler检测到的state状态都是最新的,至于volatile关键字的用法,建议参考:Java并发编程:volatile关键字解析

当Channel检测到交换机连接时,会调用OFChannelHandler类中的channelConnected函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@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);
}

在这里,state被设置为ChannelState.WAIT_HELLO状态,顾名思义,就是等待交换机的hello消息了。当Channel得到交换机发往控制器的消息时,就会调用messageReceived函数,messageReceived又会调用当前状态机实例的processOFMessage方法,处理OFMessage消息,各种不同的OFMessage消息对应了不同的消息处理函数,在ChannelState枚举类中都有定义。需要注意的是,不同的状态机实例可能根据当前状态重写ChannelState中的消息处理函数,因此同一消息类型在不同的状态下的处理逻辑可能是不一样的。OFMessage处理完成后,调用setState方法来切换状态机,握手成功后,状态机处于ACTIVE(true)状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@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());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
/**
* Process an OF message received on the channel and
* update state accordingly.
*
* The main "event" of the state machine. Process the received message,
* send follow up message if required and update state if required.
*
* Switches on the message type and calls more specific event handlers
* for each individual OF message type. If we receive a message that
* is supposed to be sent from a controller to a switch we throw
* a SwitchStateExeption.
*
* The more specific handlers can also throw SwitchStateExceptions
*
* @param h The OFChannelHandler that received the message
* @param m The message we received.
* @throws SwitchStateException if the switch is not bound to the channel
* @throws IOException if unable to send message back to the switch
*/
void processOFMessage(OFChannelHandler h, OFMessage m)
throws IOException, SwitchStateException {
switch(m.getType()) {
case HELLO:
processOFHello(h, (OFHello) m);
break;
case BARRIER_REPLY:
processOFBarrierReply(h, (OFBarrierReply) m);
break;
case ECHO_REPLY:
processOFEchoReply(h, (OFEchoReply) m);
break;
case ECHO_REQUEST:
processOFEchoRequest(h, (OFEchoRequest) m);
break;
case ERROR:
processOFError(h, (OFErrorMsg) m);
break;
case FEATURES_REPLY:
processOFFeaturesReply(h, (OFFeaturesReply) m);
break;
case FLOW_REMOVED:
processOFFlowRemoved(h, (OFFlowRemoved) m);
break;
case GET_CONFIG_REPLY:
processOFGetConfigReply(h, (OFGetConfigReply) m);
break;
case PACKET_IN:
processOFPacketIn(h, (OFPacketIn) m);
break;
case PORT_STATUS:
processOFPortStatus(h, (OFPortStatus) m);
break;
case QUEUE_GET_CONFIG_REPLY:
processOFQueueGetConfigReply(h, (OFQueueGetConfigReply) m);
break;
case STATS_REPLY: // multipart_reply in 1.3
processOFStatisticsReply(h, (OFStatsReply) m);
break;
case EXPERIMENTER:
processOFExperimenter(h, (OFExperimenter) m);
break;
case ROLE_REPLY:
processOFRoleReply(h, (OFRoleReply) m);
break;
case GET_ASYNC_REPLY:
processOFGetAsyncReply(h, (OFAsyncGetReply) m);
break;

// The following messages are sent to switches. The controller
// should never receive them
case SET_CONFIG:
case GET_CONFIG_REQUEST:
case PACKET_OUT:
case PORT_MOD:
case QUEUE_GET_CONFIG_REQUEST:
case BARRIER_REQUEST:
case STATS_REQUEST: // multipart request in 1.3
case FEATURES_REQUEST:
case FLOW_MOD:
case GROUP_MOD:
case TABLE_MOD:
case GET_ASYNC_REQUEST:
case SET_ASYNC:
case METER_MOD:
default:
illegalMessageReceived(h, m);
break;
}
}

以上就是状态机启动的基本流程了,更详细的过程看OFChannelHandler源码。

参考:

ONOS中Channel状态机分析

ONOS中控制器与交换机建立连接的过程

OpenFlow state machine