通过这几天对openvswitch代码的分析,以及项目的须要,须要对openflow 1.0进行一定的扩展,发现网上没有这方面的教程,尽管在搞懂ovs代码架构,floodlight controller中利用的事件驱动模型之后,会认为并非难事,可是对于刚入门SDN的同学来说,须要一番折腾,这里简单记录一下,希望帮助到其它人。
环境配置:2host + 1 OVS + floodlight
软件版本号: openvswitch 1.9.0 , floodlight0.85
在尝试对ovs中的openflow协议进行扩展之前至少应该理清的是,ovs怎样与SDN controller进行通信,收发of msg的过程,处理of action的流程,特别是怎样解析来自controller的消息;此外由于Floodlight利用的是netty框架,所以还要理解netty事件驱动的原理。接下来添加一个简单的openflow message以及相应的action ,然后看ovs能否成功接收来自controller的消息。
1.在 include/openflow/openflow-1.0.h中扩展of协议,添加我们的消息类型。(里面的字段依据自己的业务逻辑须要而设置)
struct ofp10_action_fp_update {
ovs_be16 type;
ovs_be16 len;
ovs_be32 vector;
};
OFP_ASSERT(sizeof(struct ofp10_action_fp_update) == 8);
struct ofp_fp_update{
ovs_be32 buffer_id; /* ID assigned by datapath or UINT32_MAX. */
ovs_be16 in_port; /* Packet's input port (OFPP_NONE if none). */
ovs_be16 other; /* other fields reserved. */
struct ofp_action_header actions[0]; /* The action length is inferred
from the length field in the
header. */
};
OFP_ASSERT(sizeof(struct ofp_fp_update) == 8);
2.在 lib/ofp-msg.h 更新ofpraw这个结构体(非常重要,由于当ovs收到来自controller的消息之后就会利用ofpraw里面格式化的定义来验证消息的有效性,从而进行兴许处理)和ofptype枚举体。须要注意的是对ofpraw的更改一定要遵循固定的格式,凝视中讲的非常清楚。
enum ofpraw {
/* Immutable standard messages.
*
* The OpenFlow standard promises to preserve these messages and their numbers
* in future versions, so we mark them as <all>, which covers every OpenFlow
* version numbered 0x01...0xff, rather than as OF1.0+, which covers only
* OpenFlow versions that we otherwise implement.
* Without <all> here, then we would fail to decode "hello" messages that
* announce a version higher than we understand, even though there still could
* be a version in common with the peer that we do understand. The <all>
* keyword is less useful for the other messages, because our OpenFlow channels
* accept only OpenFlow messages with a previously negotiated version.
*/
....................................................
/* OFPT 1.0 (13): struct ofp_packet_out, uint8_t[]. */
OFPRAW_OFPT10_PACKET_OUT,
/* OFPT 1.1+ (13): struct ofp11_packet_out, uint8_t[]. */
OFPRAW_OFPT11_PACKET_OUT,
/* OFPT 1.0 (20): struct ofp_fp_update, struct ofp_action_header[]. */
OFPRAW_OFPT10_FP_UPDATE,
};
enum ofptype {
/* Immutable messages. */
OFPTYPE_HELLO, /* OFPRAW_OFPT_HELLO. */
OFPTYPE_ERROR, /* OFPRAW_OFPT_ERROR. */
......................................
/* Controller command messages. */
OFPTYPE_PACKET_OUT, /* OFPRAW_OFPT10_PACKET_OUT.
* OFPRAW_OFPT11_PACKET_OUT. */
OFPTYPE_FLOW_MOD, /* OFPRAW_OFPT10_FLOW_MOD.
* OFPRAW_OFPT11_FLOW_MOD.
* OFPRAW_NXT_FLOW_MOD. */
OFPTYPE_PORT_MOD, /* OFPRAW_OFPT10_PORT_MOD.
* OFPRAW_OFPT11_PORT_MOD. */
OFPTYPE_FP_UPDATE, /* OFPRAW_OFPT10_FP_UPDATE. */
};
3. 在lib/ofp-util.h 中加入对应的数据结构代表对应的通用类型结构,由于ovs用户空间在解码对应的消息的时候须要针对详细的类型运行对应的动作,所以也要更新 ofp-actions.h
// ofp-util.h
struct ofputil_fp_update {
struct ofpact *ofpacts; /* Actions. */
size_t ofpacts_len; /* Size of ofpacts in bytes. */
};
// 对这个消息编解码辅助函数
enum ofperr ofputil_decode_fp_update(struct ofputil_fp_update *,
const struct ofp_header *,
struct ofpbuf *ofpacts);
struct ofpbuf *ofputil_encode_fp_update(const struct ofputil_fp_update *,
enum ofputil_protocol protocol);
// ofp-actions.h
struct ofpact_fp_update {
struct ofpact ofpact;
uint32_t vector; /* 自己定义*/
};
4. 在 ofproto/ofproto.c (3790)- handle_openflow_() 函数中加入处理该消息的分支,
static enum ofperr
3793 handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
3794 {
3795 const struct ofp_header *oh = msg->data;
3796 enum ofptype type;
3797 enum ofperr error;
3798
3799 error = ofptype_decode(&type, oh);
3800 if (error) {
3801 return error;
3802 }
3803
3804 switch (type) {
3805 /* OpenFlow requests. */
3806 case OFPTYPE_ECHO_REQUEST:
3807 return handle_echo_request(ofconn, oh);
3808
3809 case OFPTYPE_FEATURES_REQUEST:
3810 return handle_features_request(ofconn, oh);
3811
3812 case OFPTYPE_GET_CONFIG_REQUEST:
3813 return handle_get_config_request(ofconn, oh);
3814
3815 case OFPTYPE_SET_CONFIG:
3816 return handle_set_config(ofconn, oh);
3817
3818 case OFPTYPE_PACKET_OUT:
3819 return handle_packet_out(ofconn, oh);
3820 case OFPTYPE_FP_UPDATE:
3821 return handle_fp_update(ofconn, oh);
...............................................
}
处理函数自己依据须要来实现,这里只打印日志信息。
static enum ofperr
handle_fp_update(struct ofconn *ofconn, const struct ofp_header *oh)
{
//TODO
VLOG_INFO("=====I GOT FP FROM CONTROLLER ========");
return 0;
}
以上就是ovs上须要考虑的地方,细节处要注意。接下来是Floodlight上面通过构造一个该消息,而后当接收到packet in 后就将其发送到这个SW上。
5. 在org.openflow.protocol 包中添加我们的消息类,以及对应的方法。
public class OFFPUpdate extends OFMessage implements OFActionFactoryAware{
public static int MINIMUM_LENGTH = 16;
public static int BUFFER_ID_NONE = 0xffffffff;
protected OFActionFactory actionFactory;
protected int bufferId;
protected short inPort = 0x0000; // not used yet
protected short other;
protected List<OFAction> actions;
public OFFPUpdate(){
super();
this.type = OFType.FP_UPDATE;
this.length = U16.t(MINIMUM_LENGTH);
}
....................................
}
6.然后再OFType中标示这个新增的消息类。
public enum OFType {
HELLO (0, OFHello.class, new Instantiable<OFMessage>() {
@Override
public OFMessage instantiate() {
return new OFHello();
}}),
...........................................................
FP_UPDATE (20, OFFPUpdate.class, new Instantiable<OFMessage>() {
@Override
public OFMessage instantiate() {
return new OFFPUpdate();
}});
}
7. 在org.openflow.protocol.action中新增相应的action类,由于每次从SDN controller发送控制类消息时都会设置它的action list。
public class OFActionFPUpdate extends OFAction implements Cloneable {
public static int MINIMUM_LENGTH = 8;
protected int vector;
public OFActionFPUpdate() {
super.setType(OFActionType.FP_UPDATE);
super.setLength((short) MINIMUM_LENGTH);
}
public OFActionFPUpdate(int vector) {
super();
super.setType(OFActionType.FP_UPDATE);
super.setLength((short) MINIMUM_LENGTH);
this.vector = vector;
}
...................................
}
8. 最后写一个简单的module 来測试。这里的思路是当收到某个SW发来的packetin消息时,我们就给这个交换机下发一个FPUpdate的消息。主要代码:
OFFPUpdate fu =
(OFFPUpdate) floodlightProvider.getOFMessageFactory()
.getMessage(OFType.FP_UPDATE);
OFActionFPUpdate action = new OFActionFPUpdate();
action.setVector(0xffffffff); // just for testing
List<OFAction> actions = new ArrayList<OFAction>();
actions.add(action);
fu.setBufferId(OFFPUpdate.BUFFER_ID_NONE)
.setInPort((short)0)
.setActions(actions)
.setLengthU(OFFPUpdate.MINIMUM_LENGTH+OFActionFPUpdate.MINIMUM_LENGTH);
try {
sw.write(fu, cntx);
} catch (IOException e) {
logger.error("Failure writing fp update", e);
}
通过简单的測试,整个流程是正确的。
转载请注明出处:点击打开链接