ovs 添加流表流程

可以通过controller或者ovs-ofctl命令给网桥添加流表,这篇文章以ovs-ofctl添加流表为例,看一下如何解析匹配域和action,如何发送openflow消息给网桥及ovs-vswitchd后台进程如何处理openflow消息。

添加流表格式

###添加流表时,都会调用 ofputil_parse_key_value 解析匹配域和动作域,格式如下
a. 可以只指定key,如下命令,tcp 指定协议,actions 中的1表示出端口为1
    ovs-ofctl add-flow br0 "table=0 , priority=50, tcp, actions=1"
b. 指定key-value对,key-value对中间可以使用"=",":"或者使用小括号将value括起来key(value)
    ovs-ofctl add-flow br0 "table=0 , priority=50, ct_state=-trk, tcp, in_port:1, actions=output:2"
    ovs-ofctl add-flow br0 "table=0 , priority=50, ct_state=-trk, tcp, in_port=1, actions=output=2"
    ovs-ofctl add-flow br0 "table=0 , priority=50, ct_state=-trk, tcp, in_port(1), actions=output(2)"

ovs支持的匹配字段

//初始化时,将支持的字段,按照name计算hash值后,保存到全局变量 mf_by_name。
//解析匹配字段时,可以通过name查找是否支持此字段
enum OVS_PACKED_ENUM mf_field_id {
/* ## -------- ## */
/* ## Metadata ## */
/* ## -------- ## */
    /* "dp_hash".
     *
     * Flow hash computed in the datapath.  Internal use only, not programmable
     * from controller.
     *
     * The OXM code point for this is an attempt to test OXM experimenter
     * support, which is otherwise difficult to test due to the dearth of use
     * out in the wild.  Because controllers can't add flows that match on
     * dp_hash, this doesn't commit OVS to supporting this OXM experimenter
     * code point in the future.
     *
     * Type: be32.
     * Maskable: bitwise.
     * Formatting: hexadecimal.
     * Prerequisites: none.
     * Access: read-only.
     * NXM: NXM_NX_DP_HASH(35) since v2.2.
     * OXM: NXOXM_ET_DP_HASH(0) since OF1.5 and v2.4.
     */
    MFF_DP_HASH,
    ...
    MFF_N_IDS
};
nxm_init() -> nxm_do_init
    shash_init(&mf_by_name);
    for (i = 0; i < MFF_N_IDS; i++) {
        //mf_fields 全局变量,包含所有支持的匹配字段
        const struct mf_field *mf = &mf_fields[i];

        ovs_assert(mf->id == i); /* Fields must be in the enum order. */

        shash_add_once(&mf_by_name, mf->name, mf);
        if (mf->extra_name) {
            shash_add_once(&mf_by_name, mf->extra_name, mf);
        }
    }

//mf_fields 全局变量的定义,包含了文件 meta-flow.inc,此文件是在编译时通过python脚本自动生成,
//其中定义了支持的所有匹配字段
const struct mf_field mf_fields[MFF_N_IDS] = {
#include "meta-flow.inc"
};
//meta-flow.inc 如下,摘取此文件
{
    MFF_DP_HASH,
    "dp_hash", NULL,
    4, 32, false,
    MFM_FULLY, MFS_HEXADECIMAL, MFP_NONE, false, false,
    OFPUTIL_P_NXM_OXM_ANY,
    OFPUTIL_P_NXM_OXM_ANY,
    OFPUTIL_P_NXM_OXM_ANY,
    -1, /* not usable for prefix lookup */
},
{
    MFF_RECIRC_ID,
    "recirc_id", NULL,
    4, 32, false,
    MFM_NONE, MFS_DECIMAL, MFP_NONE, false, false,
    OFPUTIL_P_NXM_OXM_ANY,
    OFPUTIL_P_NONE,
    OFPUTIL_P_NONE,
    -1, /* not usable for prefix lookup */
},
{
    MFF_PACKET_TYPE,
    "packet_type", NULL,
    4, 32, false,
    MFM_NONE, MFS_PACKET_TYPE, MFP_NONE, false, false,
    OFPUTIL_P_NXM_OXM_ANY,
    OFPUTIL_P_NONE,
    OFPUTIL_P_NONE,
    -1, /* not usable for prefix lookup */
},

ovs支持的action

支持的大部分action都定义在宏 OFPACTS 中,它引用的另一个宏 OFPACT 在不同函数中定义不同。
宏 OFPACT 的四个参数分别表示:
    ENUM -- 用来定义 type OFPACT_ENUM,
    STRUCT -- 结构体定义,用来保存 action 字段,
    MEMBER -- 结构体中的一个字段,
    NAME -- action的名字。

#define OFPACTS                                                         \
    /* Output. */                                                       \
    OFPACT(OUTPUT,          ofpact_output,      ofpact, "output")       \
    OFPACT(GROUP,           ofpact_group,       ofpact, "group")        \
    OFPACT(CONTROLLER,      ofpact_controller,  userdata, "controller") \
    OFPACT(ENQUEUE,         ofpact_enqueue,     ofpact, "enqueue")      \
    OFPACT(OUTPUT_REG,      ofpact_output_reg,  ofpact, "output_reg")   \
    OFPACT(BUNDLE,          ofpact_bundle,      slaves, "bundle")       \
    ...

//比如下面的函数,这里的宏 OFPACT 用来定义 type
/* enum ofpact_type, with a member OFPACT_<ENUM> for each action. */
enum OVS_PACKED_ENUM ofpact_type {
#define OFPACT(ENUM, STRUCT, MEMBER, NAME) OFPACT_##ENUM,
    OFPACTS
#undef OFPACT
};

展开后 ofpact_type 定义如下
enum OVS_PACKED_ENUM ofpact_type {
    OFPACT_OUTPUT,
    OFPACT_GROUP,
    OFPACT_CONTROLLER,
    OFPACT_ENQUEUE,
    ...
    OFPACT_CT
    ...
};

//比如下面的函数,这里的宏 OFPACT 用来根据 name 判断是否有匹配的action,如果有则设置type,并返回true
static bool
ofpact_type_from_name(const char *name, enum ofpact_type *type)
{
#define OFPACT(ENUM, STRUCT, MEMBER, NAME)                            \
    if (!strcasecmp(name, NAME)) {                                    \
        *type = OFPACT_##ENUM;                                          \
        return true;                                                    \
    }
    OFPACTS
#undef OFPACT

    return false;
}

//比如下面的函数,这里的宏 OFPACT 用来根据 type 返回对应的name
const char *
ofpact_name(enum ofpact_type type)
{
    switch (type) {
#define OFPACT(ENUM, STRUCT, MEMBER, NAME) case OFPACT_##ENUM: return NAME;
        OFPACTS
#undef OFPACT
    }
    return "<unknown>";
}

//比如下面的函数,这里的宏 OFPACT 用来根据 type 执行对应的函数,解析action
static char * OVS_WARN_UNUSED_RESULT
ofpact_parse(enum ofpact_type type, char *value,
             const struct ofputil_port_map *port_map, struct ofpbuf *ofpacts,
             enum ofputil_protocol *usable_protocols)
{
    switch (type) {
#define OFPACT(ENUM, STRUCT, MEMBER, NAME)                              \
        case OFPACT_##ENUM:                                             \
            return parse_##ENUM(value, port_map, ofpacts, usable_protocols);
        OFPACTS
#undef OFPACT
    default:
        OVS_NOT_REACHED();
    }
}

ovs-ofctl add-flow 添加流表流程

ofctl_add_flow -> ofctl_flow_mod

static void
ofctl_flow_mod(int argc, char *argv[], uint16_t command)
{
    if (argc > 2 && !strcmp(argv[2], "-")) {
        ofctl_flow_mod_file(argc, argv, command);
    } else {
        struct ofputil_flow_mod fm;
        char *error;
        enum ofputil_protocol usable_protocols;
        //解析命令行指定的流表信息,保存到 struct ofputil_flow_mod fm 中,主要包含匹配字段和action。
        //根据匹配字段和action,得出最低支持的协议版本号
        error = parse_ofp_flow_mod_str(&fm, argc > 2 ? argv[2] : "",
                                       ports_to_accept(argv[1]), command,
                                       &usable_protocols);
        if (error) {
            ovs_fatal(0, "%s", error);
        }
        //将 struct ofputil_flow_mod fm 中的信息转换成 openflow 消息,并发送
        ofctl_flow_mod__(argv[1], &fm, 1, usable_protocols);
    }
}

下面分别看一下函数parse_ofp_flow_mod_str和ofctl_flow_mod__的实现。
parse_ofp_flow_mod_str
parse_ofp_flow_mod_str -> parse_ofp_str -> parse_ofp_str__解析命令行指定的匹配域和action

static char * OVS_WARN_UNUSED_RESULT
parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string,
                const struct ofputil_port_map *port_map,
                enum ofputil_protocol *usable_protocols)
    *fm = (struct ofputil_flow_mod) {
        .match = MATCH_CATCHALL_INITIALIZER,
        .priority = OFP_DEFAULT_PRIORITY,
        .table_id = 0xff,
        .command = command,
        .buffer_id = UINT32_MAX,
        .out_port = OFPP_ANY,
        .out_group = OFPG_ANY,
    };
    //将 action= 后面的字符串保存在 act_str 中
    act_str = extract_actions(string);

    //解析匹配域,保存到 fm->match
    while (ofputil_parse_key_value(&string, &name, &value)) {
        const struct protocol *p;
        const struct mf_field *mf;

        //解析协议相关的字段
        if (parse_protocol(name, &p)) {
            match_set_dl_type(&fm->match, htons(p->dl_type));
            if (p->nw_proto) {
                match_set_nw_proto(&fm->match, p->nw_proto);
            }
            match_set_default_packet_type(&fm->match);
        } else if (!strcmp(name, "eth")) {
            match_set_packet_type(&fm->match, htonl(PT_ETH));
        //到hash表 mf_by_name 查找是否支持name指定的字段
        } else if ((mf = mf_from_name(name)) != NULL) {
            parse_field(mf, value, port_map, &fm->match, usable_protocols);
                union mf_value value, mask;
                //解析 value,并设置相应的 mask
                mf_parse(mf, s, port_map, &value, &mask);
                //将value保存到 match 中
                mf_set(mf, &value, &mask, match, &error);
                    if (!mask || is_all_ones(mask, mf->n_bytes)) {
                        mf_set_value(mf, value, match, err_str);
                        return mf->usable_protocols_exact;
                    } else if (is_all_zeros(mask, mf->n_bytes) && !mf_is_tun_metadata(mf)) {
                        /* Tunnel metadata matches on the existence of the field itself, so
                         * it still needs to be encoded even if the value is wildcarded. */
                        mf_set_wild(mf, match, err_str);
                        return OFPUTIL_P_ANY;
                    }
                    switch (mf->id) {
                    case MFF_CT_ZONE:
                    case MFF_CT_NW_PROTO:
                        return OFPUTIL_P_NONE;
                    ...
                    case MFF_CT_STATE:
                        match_set_ct_state_masked(match, ntohl(value->be32), ntohl(mask->be32));
                        break;
                    }
        }
        ...
    }

    //解析action,保存到 fm->ofpacts
    enum ofputil_protocol action_usable_protocols;
    struct ofpbuf ofpacts;
    ofpbuf_init(&ofpacts, 32);
    ofpacts_parse_instructions(act_str, port_map, &ofpacts, &action_usable_protocols);
        ofpacts_parse_copy(s, port_map, ofpacts, usable_protocols, true, 0);
            *usable_protocols = OFPUTIL_P_ANY;
            ofpacts_parse(s, port_map, ofpacts, usable_protocols, allow_instructions, outer_action);
                ofpacts_parse__(str, port_map, ofpacts, usable_protocols, allow_instructions, outer_action);
                    pos = str;
                    //解析key value
                    while (ofputil_parse_key_value(&pos, &key, &value)) {
                        enum ovs_instruction_type inst = OVSINST_OFPIT11_APPLY_ACTIONS;
                        enum ofpact_type type;
                        char *error = NULL;
                        ofp_port_t port;
                        //根据key查找是否支持此action,并返回 type
                        if (ofpact_type_from_name(key, &type)) {
                            //根据 type,解析action,如果 type 为 OFPACT_CT,则执行 parse_CT
                            ofpact_parse(type, value, port_map, ofpacts, usable_protocols);
                                parse_CT(value, port_map, ofpacts, usable_protocols);
                            inst = ovs_instruction_type_from_ofpact_type(type);
                        } else if (!strcasecmp(key, "mod_vlan_vid")) {
                            error = parse_set_vlan_vid(value, ofpacts, true);
                        } else if (!strcasecmp(key, "mod_vlan_pcp")) {
                            error = parse_set_vlan_pcp(value, ofpacts, true);
                        } else if (!strcasecmp(key, "set_nw_ttl")) {
                            error = parse_SET_IP_TTL(value, port_map,
                                                     ofpacts, usable_protocols);
                        } else if (!strcasecmp(key, "pop_vlan")) {
                            error = parse_pop_vlan(ofpacts);
                        } else if (!strcasecmp(key, "set_tunnel64")) {
                            error = parse_set_tunnel(value, ofpacts,
                                                     NXAST_RAW_SET_TUNNEL64);
                        } else if (!strcasecmp(key, "load")) {
                            error = parse_reg_load(value, ofpacts);
                        } else if (!strcasecmp(key, "bundle_load")) {
                            error = parse_bundle_load(value, port_map, ofpacts);
                        } else if (!strcasecmp(key, "drop")) {
                            drop = true;
                        } else if (!strcasecmp(key, "apply_actions")) {
                            return xstrdup("apply_actions is the default instruction");
                        //解析出端口,可以指定端口号,也可以指定端口名字
                        } else if (ofputil_port_from_string(key, port_map, &port)) {
                            ofpact_put_OUTPUT(ofpacts)->port = port;
                        } else {
                            return xasprintf("unknown action %s", key);
                        }
                        ...
                    }
    //将解析的action保存到 fm->ofpacts
    fm->ofpacts_len = ofpacts.size;
    fm->ofpacts = ofpbuf_steal_data(&ofpacts);

ofctl_flow_mod__
ofctl_flow_mod__ 将 struct ofputil_flow_mod fm 中的信息转换成 openflow 消息,并将消息发送出去

static void
ofctl_flow_mod__(const char *remote, struct ofputil_flow_mod *fms,
                 size_t n_fms, enum ofputil_protocol usable_protocols)
{
    enum ofputil_protocol protocol;
    struct vconn *vconn;
    size_t i;

    if (bundle) {
        bundle_flow_mod__(remote, fms, n_fms, usable_protocols);
        return;
    }

    protocol = open_vconn_for_flow_mod(remote, &vconn, usable_protocols);

    for (i = 0; i < n_fms; i++) {
        struct ofputil_flow_mod *fm = &fms[i];
        //ofputil_encode_flow_mod 封装openflow消息,transact_noreply将消息发送出去
        transact_noreply(vconn, ofputil_encode_flow_mod(fm, protocol));
        free(CONST_CAST(struct ofpact *, fm->ofpacts));
    }
    vconn_close(vconn);
}

//将 struct ofputil_flow_mod fm 中的信息转换成 openflow 消息
struct ofpbuf *
ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm, enum ofputil_protocol protocol)
    //不同的协议版本有不同的处理
    switch (protocol) {
    case OFPUTIL_P_OF11_STD:
    case OFPUTIL_P_OF12_OXM:
    case OFPUTIL_P_OF13_OXM:
    case OFPUTIL_P_OF14_OXM:
    case OFPUTIL_P_OF15_OXM:
    case OFPUTIL_P_OF16_OXM: {
        struct ofp11_flow_mod *ofm;
        ...
        break;
    }
    case OFPUTIL_P_OF10_STD:
    case OFPUTIL_P_OF10_STD_TID: {
        struct ofp10_flow_mod *ofm;
        ...
        break;
    }

    case OFPUTIL_P_OF10_NXM:
    case OFPUTIL_P_OF10_NXM_TID: {
        struct nx_flow_mod *nfm;
        int match_len;
        //分配内存,将openflow信息保存到 struct nx_flow_mod
        msg = ofpraw_alloc(OFPRAW_NXT_FLOW_MOD, OFP10_VERSION,
                           NXM_TYPICAL_LEN + fm->ofpacts_len);
            ofpraw_alloc_xid(raw, version, alloc_xid(), extra_tailroom);
                struct ofpbuf *buf = ofpbuf_new(0);
                ofpraw_put__(raw, version, xid, extra_tailroom, buf);
                    //这里用到另一个自动生成的全局变量 raw_infos
                    const struct raw_info *info = raw_info_get(raw);
                    const struct raw_instance *instance = raw_instance_get(info, version);
                    const struct ofphdrs *hdrs = &instance->hdrs;
                    struct ofp_header *oh;

                    buf->header = ofpbuf_put_uninit(buf, instance->hdrs_len);
                    buf->msg = ofpbuf_tail(buf);
                    //填充 struct ofphdrs
                    oh = buf->header;
                    oh->version = version;
                    oh->type = hdrs->type; 
                    oh->length = htons(buf->size);
                    oh->xid = xid;
                    
                    #define OF_VENDOR_ID    0
                    #define HPL_VENDOR_ID   0x000004EA /* HP Labs. */
                    #define NTR_VENDOR_ID   0x0000154d /* Netronome. */
                    #define NTR_COMPAT_VENDOR_ID   0x00001540 /* Incorrect value used in v2.4. */
                    #define NX_VENDOR_ID    0x00002320 /* Nicira. */
                    #define ONF_VENDOR_ID   0x4f4e4600 /* Open Networking Foundation. */
                    #define INTEL_VENDOR_ID 0x0000AA01 /* Intel */
                    //如果 hdrs->type 为 OFPT_VENDOR,说明是厂商自定义的类型,
                    //需要通过 vendor 指定厂商id(上面的宏定义),并通过 subtype 指定真正的消息类型
                    if (hdrs->type == OFPT_VENDOR) {
                        struct ofp_vendor_header *ovh = buf->header;
                        ovh->vendor = htonl(hdrs->vendor);
                        ovh->subtype = htonl(hdrs->subtype);
                    }

        nfm = ofpbuf_put_zeros(msg, sizeof *nfm);
        nfm->command = ofputil_tid_command(fm, protocol);
        nfm->cookie = fm->new_cookie;
        //处理 match 匹配域
        match_len = nx_put_match(msg, &fm->match, fm->cookie, fm->cookie_mask);
        nfm = msg->msg;
        nfm->idle_timeout = htons(fm->idle_timeout);
        nfm->hard_timeout = htons(fm->hard_timeout);
        nfm->priority = htons(fm->priority);
        nfm->buffer_id = htonl(fm->buffer_id);
        nfm->out_port = htons(ofp_to_u16(fm->out_port));
        nfm->flags = raw_flags;
        nfm->match_len = htons(match_len);
        //处理 action
        ofpacts_put_openflow_actions(fm->ofpacts, fm->ofpacts_len, msg, version);
        break;
    }

    default:
        OVS_NOT_REACHED();
    }   
    ofpmsg_update_length(msg);
        struct ofp_header *oh = ofpbuf_at_assert(buf, 0, sizeof *oh);
        oh->length = htons(buf->size);
    return msg;

ovs-vswitchd 接收openflow消息,添加流表流程

接收到openflow消息后会调用handle_openflow__进行处理,其中参数msg->data 指向openflow消息,包括ofp_header和消息体。
openflow消息格式如下(以ct消息为例)

struct ofp_vendor_header(struct ofp_header+vendor+subtype) + struct nx_flow_mod + match field(header+payload) + 
match field(header+payload) + struct ofp_action_header + struct nx_action_conntrack

static enum ofperr
handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
    OVS_EXCLUDED(ofproto_mutex)
{
    const struct ofp_header *oh = msg->data;
    enum ofptype type;
    enum ofperr error;
    //解析openflow消息类型,不是直接用 oh->type,oh->type不是真正的type
    error = ofptype_decode(&type, oh);
    if (error) {
        return error;
    }

    switch (type) {
    //添加流表消息类型为 OFPTYPE_FLOW_MOD,调用handle_flow_mod处理
    case OFPTYPE_FLOW_MOD:
        return handle_flow_mod(ofconn, oh);
    }
}

handle_flow_mod首先解码openflow消息到struct ofputil_flow_mod,再添加流表。

static enum ofperr
handle_flow_mod(struct ofconn *ofconn, const struct ofp_header *oh)
    OVS_EXCLUDED(ofproto_mutex)
{
    struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
    struct ofputil_flow_mod fm;
    uint64_t ofpacts_stub[1024 / 8];
    struct ofpbuf ofpacts;
    enum ofperr error;

    error = reject_slave_controller(ofconn);
    if (error) {
        return error;
    }

    ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
    //解码openflow消息,保存到 struct ofputil_flow_mod fm
    error = ofputil_decode_flow_mod(&fm, oh, ofconn_get_protocol(ofconn),
                                    ofproto_get_tun_tab(ofproto),
                                    &ofproto->vl_mff_map, &ofpacts,
                                    u16_to_ofp(ofproto->max_ports),
                                    ofproto->n_tables);
    if (!error) {
        struct openflow_mod_requester req = { ofconn, oh };
        //处理fm
        error = handle_flow_mod__(ofproto, &fm, &req);
    }

    ofpbuf_uninit(&ofpacts);
    return error;
}

下面分别看一下ofputil_decode_flow_mod和handle_flow_mod__
ofputil_decode_flow_mod

enum ofperr
ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
                        const struct ofp_header *oh,
                        enum ofputil_protocol protocol,
                        const struct tun_table *tun_table,
                        const struct vl_mff_map *vl_mff_map,
                        struct ofpbuf *ofpacts,
                        ofp_port_t max_port, uint8_t max_table)
    struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
    //pull后,跳过 struct ofp_vendor_header,b->data 指向消息体
    //header中 raw 表示类型
    enum ofpraw raw = ofpraw_pull_assert(&b);

    if (raw == OFPRAW_OFPT11_FLOW_MOD) {
        ...
    }
    else {
        if (raw == OFPRAW_OFPT10_FLOW_MOD) {
            ...
        } else if (raw == OFPRAW_NXT_FLOW_MOD) {
            /* Nicira extended flow_mod. */
            const struct nx_flow_mod *nfm;

            /* Dissect the message. */
            //返回 nfm。b->data 向后偏移 sizeof *nfm 个字节,指向match field
            nfm = ofpbuf_pull(&b, sizeof *nfm);
            nx_pull_match(&b, ntohs(nfm->match_len), &fm->match, &fm->cookie, &fm->cookie_mask, false, tun_table, vl_mff_map);
                nx_pull_match__(b, match_len, true, pipeline_fields_only, match, cookie, cookie_mask, tun_table, vl_mff_map);
                    uint8_t *p = NULL;
                    //返回p,指向 match field 开始位置,b->data 向后偏移 match_len
                    if (match_len) {
                        p = ofpbuf_try_pull(b, ROUND_UP(match_len, 8));
                    }
                    //遍历p指向的match field,设置到对应的 match 字段中
                    nx_pull_raw(p, match_len, strict, pipeline_fields_only, match, cookie, cookie_mask, tun_table, vl_mff_map);
                        struct ofpbuf b = ofpbuf_const_initializer(p, match_len);
                        while (b.size) {
                            const uint8_t *pos = b.data;
                            const struct mf_field *field;
                            union mf_value value;
                            union mf_value mask;

                            nx_pull_match_entry(&b, cookie != NULL, vl_mff_map, &field, &value, &mask);
                                //header 格式 oxm_class+oxm_field+hm+oxm_length+payload,其中hm全称为hasmaks,表示是否有掩码
                                //没有掩码时:oxm_class+oxm_field+0+oxm_length+payload
                                //有掩码时:  oxm_class+oxm_field+1+oxm_length+payload+mask
                                //如果 oxm_class 为 oxffff,则说明是 experimenter,格式变成
                                //oxm_class+oxm_field+hm+oxm_length+experimenter ID+payload
                                uint64_t header;
                                nx_pull_entry__(b, allow_cookie, vl_mff_map, &header, field, value, mask);
                                    const struct mf_field *field;
                                    enum ofperr header_error;
                                    unsigned int payload_len;
                                    const uint8_t *payload;
                                    int width;

                                    nx_pull_header__(b, allow_cookie, vl_mff_map, header, &field);
                                        //先判断是否是 experimenter_oxm,如果是,则取64位值
                                        *header = ((uint64_t) ntohl(get_unaligned_be32(b->data))) << 32;
                                        if (is_experimenter_oxm(*header)) {
                                            *header = ntohll(get_unaligned_be64(b->data));
                                        }
                                        ofpbuf_pull(b, nxm_header_len(*header));
                                        *field = mf_from_oxm_header(*header, vl_mff_map);
                                            //遍历 nxm_header_map 找到 struct nxm_field
                                            const struct nxm_field *f = nxm_field_by_header(header);
                                            const struct mf_field *mff = mf_from_id(f->id);
                                                //返回 mf_field
                                                return &mf_fields[id];
                                            const struct mf_field *vl_mff = mf_get_vl_mff(mff, vl_mff_map);
                                            return vl_mff ? vl_mff : mff;

                                    payload_len = nxm_payload_len(*header);
                                    payload = ofpbuf_try_pull(b, payload_len);
                                    width = nxm_field_bytes(*header);
                                    //将 payload 拷贝到 value
                                    copy_entry_value(field, value, payload, width);

                                    if (mask) {
                                        if (nxm_hasmask(*header)) {
                                            //将 mask 拷贝到 mask
                                            copy_entry_value(field, mask, payload + width, width);
                                        } else {
                                            //如果没有指定mask,则设置mask全f
                                            memset(mask, 0xff, sizeof *mask);
                                        }
                                    }
                                    if (field_) {
                                        *field_ = field;
                                        return header_error;
                                    }

                            mf_set(field, &value, &mask, match, &err_str);
                                switch (mf->id) {
                                case MFF_CT_STATE:
                                    match_set_ct_state_masked(match, ntohl(value->be32), ntohl(mask->be32));
                                        match->flow.ct_state = ct_state & mask & UINT8_MAX;
                                        match->wc.masks.ct_state = mask & UINT8_MAX;
                                    break;
                                }
                        }
                            
            fm->priority = ntohs(nfm->priority);
            fm->new_cookie = nfm->cookie;
            fm->idle_timeout = ntohs(nfm->idle_timeout);
            fm->hard_timeout = ntohs(nfm->hard_timeout);
            fm->importance = 0;
            fm->buffer_id = ntohl(nfm->buffer_id);
            fm->out_port = u16_to_ofp(ntohs(nfm->out_port));
            fm->out_group = OFPG_ANY;
            raw_flags = nfm->flags;
        }
    }

    //此时 b->data 指向了 instruction 字段,即action域
    //解析的action存放在 ofpacts 中
    ofpacts_pull_openflow_instructions(&b, b.size, oh->version, vl_mff_map, &fm->ofpacts_tlv_bitmap, ofpacts);
        ofpbuf_clear(ofpacts);
        if (version == OFP10_VERSION) {
            return ofpacts_pull_openflow_actions__(openflow, instructions_len,
                                                   version,
                                                   (1u << N_OVS_INSTRUCTIONS) - 1,
                                                   ofpacts, 0, vl_mff_map,
                                                   ofpacts_tlv_bitmap);
                const struct ofp_action_header *actions;
                size_t orig_size = ofpacts->size;
                actions = ofpbuf_try_pull(openflow, actions_len);
                ofpacts_decode(actions, actions_len, version, vl_mff_map, ofpacts_tlv_bitmap, ofpacts);
                    struct ofpbuf openflow = ofpbuf_const_initializer(actions, actions_len);
                    while (openflow.size) {
                        const struct ofp_action_header *action = openflow.data;
                        enum ofp_raw_action_type raw;
                        uint64_t arg;

                        ofpact_pull_raw(&openflow, ofp_version, &raw, &arg);
                        //ofpact_decode 是自动生成的代码,位于文件 lib/ofp-actions.inc2
                        ofpact_decode(action, raw, ofp_version, arg, vl_mff_map, ofpacts_tlv_bitmap, ofpacts);
                            switch (raw) {
                            case NXAST_RAW_CT:
                                //decode_NXAST_RAW_CT 在 lib/ofp-actions.c 文件中
                                return decode_NXAST_RAW_CT(ALIGNED_CAST(const struct nx_action_conntrack *, a), version, vl_mff_map, tlv_bitmap, out);
                                    const size_t ct_offset = ofpacts_pull(out);
                                    //out 是保存action的 ofpacts
                                    struct ofpact_conntrack *conntrack = ofpact_put_CT(out);

                                    conntrack->flags = ntohs(nac->flags);
                                    decode_ct_zone(nac, conntrack, vl_mff_map, tlv_bitmap);
                                    conntrack->recirc_table = nac->recirc_table;
                                    conntrack->alg = ntohs(nac->alg);

                                    ofpbuf_pull(out, sizeof(*conntrack));
                                    struct ofpbuf openflow = ofpbuf_const_initializer(nac + 1, ntohs(nac->len) - sizeof(*nac));
                                    ofpacts_pull_openflow_actions__(&openflow, openflow.size, ofp_version,
                                                                    1u << OVSINST_OFPIT11_APPLY_ACTIONS,
                                                                    out, OFPACT_CT, vl_mff_map,
                                                                    tlv_bitmap);
                            }
                    }
            }
        }

    //保存 action
    fm->ofpacts = ofpacts->data;
    fm->ofpacts_len = ofpacts->size;
    ofputil_decode_flow_mod_flags(raw_flags, fm->command, oh->version, &fm->flags);

handle_flow_mod__
将 struct ofputil_flow_mod fm 转换成 struct ofproto_flow_mod ofm

static enum ofperr
handle_flow_mod__(struct ofproto *ofproto, const struct ofputil_flow_mod *fm,
                  const struct openflow_mod_requester *req)
    struct ofproto_flow_mod ofm;
    ofproto_flow_mod_init(ofproto, &ofm, fm, NULL);
        /* Forward flow mod fields we need later. */
        ofm->command = fm->command;
        ofm->modify_cookie = fm->modify_cookie;
        switch (ofm->command) {
        case OFPFC_ADD:
            check_buffer_id = true;
            //创建 rule
            add_flow_init(ofproto, ofm, fm);
                if (fm->table_id == 0xff) {
                    ofproto->ofproto_class->rule_choose_table(ofproto, &fm->match, &table_id);
                } else if (fm->table_id < ofproto->n_tables) {
                    table_id = fm->table_id;
                }
                struct oftable *table;
                table = &ofproto->tables[table_id];
                if (!ofm->temp_rule) {
                    struct cls_rule cr;
                    cls_rule_init(&cr, &fm->match, fm->priority);
                        cls_rule_init__(rule, priority);
                            rculist_init(&rule->node);
                            *CONST_CAST(int *, &rule->priority) = priority;
                            ovsrcu_init(&rule->cls_match, NULL);
                        minimatch_init(CONST_CAST(struct minimatch *, &rule->match), match);
                            struct miniflow tmp;

                            miniflow_map_init(&tmp, &src->wc.masks);
                            /* Allocate two consecutive miniflows. */
                            miniflow_alloc(dst->flows, 2, &tmp);
                            miniflow_init(dst->flow, &src->flow);
                            minimask_init(dst->mask, &src->wc);

                    /* Allocate new rule.  Destroys 'cr'. */
                    ofproto_rule_create(ofproto, &cr, table - ofproto->tables,
                                                fm->new_cookie, fm->idle_timeout,
                                                fm->hard_timeout, fm->flags,
                                                fm->importance, fm->ofpacts,
                                                fm->ofpacts_len,
                                                fm->match.flow.tunnel.metadata.present.map,
                                                fm->ofpacts_tlv_bitmap, &ofm->temp_rule);
                        struct rule *rule;
                        /* Allocate new rule. */
                        rule = ofproto->ofproto_class->rule_alloc();
                            struct rule_dpif *rule = xzalloc(sizeof *rule);
                            return &rule->up;

                        /* Initialize base state. */
                        *CONST_CAST(struct ofproto **, &rule->ofproto) = ofproto;
                        //匹配域
                        cls_rule_move(CONST_CAST(struct cls_rule *, &rule->cr), cr);
                        ovs_refcount_init(&rule->ref_count);
                        rule->created = rule->modified = time_msec();
                        rule->idle_timeout = idle_timeout;
                        rule->hard_timeout = hard_timeout;
                        *CONST_CAST(uint16_t *, &rule->importance) = importance;
                        rule->removed_reason = OVS_OFPRR_NONE;
                        *CONST_CAST(uint8_t *, &rule->table_id) = table_id;
                        rule->flags = flags & OFPUTIL_FF_STATE;

                        //action
                        *CONST_CAST(const struct rule_actions **, &rule->actions) = rule_actions_create(ofpacts, ofpacts_len);

                        ofproto->ofproto_class->rule_construct(rule);
                            struct rule_dpif *rule = rule_dpif_cast(rule_);
                            ovs_mutex_init_adaptive(&rule->stats_mutex);
                            rule->stats.n_packets = 0;
                            rule->stats.n_bytes = 0;
                            rule->stats.used = rule->up.modified;
                            rule->recirc_id = 0;
                            rule->new_rule = NULL;
                            rule->forward_counts = false;

                        rule->state = RULE_INITIALIZED;
                        //new_rule 为 ofm->temp_rule
                        *new_rule = rule;
                }
            break;
        }
    ovs_mutex_lock(&ofproto_mutex);
    ofm.version = ofproto->tables_version + 1;
    error = ofproto_flow_mod_start(ofproto, &ofm);
        rule_collection_init(&ofm->old_rules);
        rule_collection_init(&ofm->new_rules);
        switch (ofm->command) {
        case OFPFC_ADD:
            add_flow_start(ofproto, ofm);
                struct rule *new_rule = ofm->temp_rule;
                const struct rule_actions *actions = rule_get_actions(new_rule);
                struct oftable *table = &ofproto->tables[new_rule->table_id];
                /* Check for the existence of an identical rule.
                 * This will not return rules earlier marked for removal. */
                old_rule = rule_from_cls_rule(classifier_find_rule_exactly(&table->cls, &new_rule->cr, ofm->version));
                
                replace_rule_start(ofproto, ofm, old_rule, new_rule);
                    //获取 table
                    struct oftable *table = &ofproto->tables[new_rule->table_id];
                    //流个数加1
                    table->n_flows++;

                    /* Insert flow to ofproto data structures, so that later flow_mods may
                     * relate to it.  This is reversible, in case later errors require this to
                     * be reverted. */
                    ofproto_rule_insert__(ofproto, new_rule);
                        const struct rule_actions *actions = rule_get_actions(rule);
                        if (rule->hard_timeout || rule->idle_timeout) {
                            ovs_list_insert(&ofproto->expirable, &rule->expirable);
                        }
                        cookies_insert(ofproto, rule);
                        eviction_group_add_rule(rule);
                        if (actions->has_meter) {
                            meter_insert_rule(rule);
                        }

                    /* Make the new rule visible for classifier lookups only from the next
                     * version. */
                    //classifier_insert(struct classifier *cls, const struct cls_rule *rule, ovs_version_t version,
                    //                  const struct cls_conjunction conj[],size_t n_conj)
                    classifier_insert(&table->cls, &new_rule->cr, ofm->version, ofm->conjs, ofm->n_conjs);
                        classifier_replace(cls, rule, version, conj, n_conj);
                            struct cls_match *new;
                            struct cls_subtable *subtable;
                            struct cls_match *head;

                            new = cls_match_alloc(rule, version, conjs, n_conjs);
                            subtable = find_subtable(cls, rule->match.mask);
                                struct cls_subtable *subtable;
                                CMAP_FOR_EACH_WITH_HASH (subtable, cmap_node, minimask_hash(mask, 0),
                                                         &cls->subtables_map) {
                                    if (minimask_equal(mask, &subtable->mask)) {
                                        return subtable;
                                    }
                                }
                                
                            if (!subtable) {
                                subtable = insert_subtable(cls, rule->match.mask);
                                    size_t count = miniflow_n_values(&mask->masks);
                                    subtable = xzalloc(sizeof *subtable + MINIFLOW_VALUES_SIZE(count));
                                    cmap_init(&subtable->rules);
                                    miniflow_clone(CONST_CAST(struct miniflow *, &subtable->mask.masks), &mask->masks, count);
                            }
                            
                            head = find_equal(subtable, rule->match.flow, hash);
                            if (!head) {
                                for (i = 0; i < cls->n_tries; i++) {
                                    if (subtable->trie_plen[i]) {
                                        trie_insert(&cls->tries[i], rule, subtable->trie_plen[i]);
                                    }
                                }
                                /* Add rule to ports trie. */
                                if (subtable->ports_mask_len) {
                                    /* We mask the value to be inserted to always have the wildcarded
                                     * bits in known (zero) state, so we can include them in comparison
                                     * and they will always match (== their original value does not
                                     * matter). */
                                    ovs_be32 masked_ports = minimatch_get_ports(&rule->match);

                                    trie_insert_prefix(&subtable->ports_trie, &masked_ports,
                                                       subtable->ports_mask_len);
                                }
                                n_rules = cmap_insert(&subtable->rules, &new->cmap_node, hash);
                            }

                            /* Make rule visible to iterators (immediately). */
                            rculist_push_back(&subtable->rules_list, CONST_CAST(struct rculist *, &rule->node));

                            cls->n_rules++;
                            if (cls->publish) {
                                pvector_publish(&cls->subtables);
                            }
            break;
        }

总结

在ovs-ofctl端,首先解析命令行参数,将匹配字段和action保存到 struct ofputil_flow_mod,再将struct ofputil_flow_mod中的信息根据openflow protocol转换到不同的结构体中,比如对于OFPUTIL_P_OF10_NXM和OFPUTIL_P_OF10_NXM_TID协议类型,会转换成struct nx_flow_mod,更详细的可参考函数ofputil_encode_flow_mod。

在ovs-vswitchd端,接收openflow消息,将其解码保存到 struct ofputil_flow_mod,再转换成 struct ofproto_flow_mod。

也可参考:ovs 添加流表流程 - 简书 (jianshu.com)

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值