美文网首页
ovs 添加流表流程

ovs 添加流表流程

作者: 分享放大价值 | 来源:发表于2021-04-07 19:48 被阅读0次

    可以通过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 添加流表流程

          本文链接:https://www.haomeiwen.com/subject/yzgukltx.html