首页 > 代码库 > 对openflow 1.0协议的扩展

对openflow 1.0协议的扩展

通过这几天对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的消息。
  在  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. */
};
在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上。
在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);
     }
  ....................................
}
然后再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();
                                }});
}
在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;
    }
     ................................... 
}
最后写一个简单的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);
        }
通过简单的测试,整个流程是正确的。

对于openvswitch的代码分析,可以看看我之前的文章 OVS源码阅读