美文网首页
ovs set_field/load/move action

ovs set_field/load/move action

作者: 分享放大价值 | 来源:发表于2021-04-29 22:31 被阅读0次

    ovs提供了修改报文内容的action,比如 set_field/load/move等,本文也重点分析这三个action及其源码实现。

    ovs-action截取这三个action的基本用法,如下

    move action: src 和 dst 都必须是 field,将src的值赋值给dst。
       Syntax:
              move:src->dst
    
       Copies the named bits from field or subfield src to field or
       subfield dst. src and dst should fields or subfields in the
       syntax described under ``Field Specifications’’ above. The two
       fields or subfields must have the same width.
    
       Examples:
              •      move:reg0[0..5]->reg1[26..31] copies the six bits
                     numbered 0 through 5 in register 0 into bits 26
                     through 31 of register 1.
              •      move:reg0[0..15]->vlan_tci copies the least
                     significant 16 bits of register 0 into the VLAN TCI
                     field.
    
    set_field action: dst 必须是field,value可以是任意值
        Syntax:
            set_field:value[/mask]->dst
            
        The set_field action takes value in the customary syntax
        for field dst, e.g. 00:11:22:33:44:55 for an Ethernet address,
        and dst as the field’s name. The optional mask allows part of a
        field to be set.
    
    load action: dst 必须是field,value可以是任意值
        Syntax:
            load:value->dst
            
        The load action takes value as an integer value (in decimal or
        prefixed by 0x for hexadecimal) and dst as a field or subfield in
        the syntax described under ``Field Specifications’’ above.
    
    The following all set the Ethernet source address to 00:11:22:33:44:55:
          •      set_field:00:11:22:33:44:55->eth_src
          •      load:0x001122334455->eth_src
          •      load:0x001122334455->OXM_OF_ETH_SRC[]
    
    The following all set the multicast bit in the Ethernet destination address:
          •      set_field:01:00:00:00:00:00/01:00:00:00:00:00->eth_dst
          •      load:1->eth_dst[40]
    

    load和set_field的作用基本上是等同的,都是将任意值赋值给field,或者赋值给field的某些位,后面分析源码时会看到他们用的是同一个结构体 struct ofpact_set_field。
    move相比上面两个action,它只能将field的值赋值给另一个field,src和dst都必须是field。

    这里需要解释下field,它表示报文字段,metadata等信息,在代码中使用 struct mf_field 表示,其中枚举类型 mf_field_id 表示 field 类型。

    enum OVS_PACKED_ENUM mf_field_id {
        MFF_DP_HASH,
        MFF_RECIRC_ID,
        MFF_PACKET_TYPE,
        MFF_TUN_ID,
        MFF_METADATA,
        MFF_IN_PORT,
        ...
    }
    
    struct mf_field {
        /* Identification. */
        enum mf_field_id id;        /* MFF_*. */
        const char *name;           /* Name of this field, e.g. "eth_type". */
        const char *extra_name;     /* Alternate name, e.g. "dl_type", or NULL. */
        ....
    }
    

    有一个全局变量 mf_fields 用来保存所有的field

    mf_fields
    {{id = MFF_DP_HASH, name = 0x55555564854a "dp_hash", extra_name = 0x0, n_bytes = 4, n_bits = 32, variable_len = false,
        maskable = MFM_FULLY, string = MFS_HEXADECIMAL, prereqs = MFP_NONE, writable = false, mapped = false, usable_protocols_exact = 1004,
        usable_protocols_cidr = 1004, usable_protocols_bitwise = 1004, flow_be32ofs = -1}, 
    {id = MFF_RECIRC_ID,
        name = 0x555555648540 "recirc_id", extra_name = 0x0, n_bytes = 4, n_bits = 32, variable_len = false, maskable = MFM_NONE,
        string = MFS_DECIMAL, prereqs = MFP_NONE, writable = false, mapped = false, usable_protocols_exact = 1004, usable_protocols_cidr = 0,
        usable_protocols_bitwise = 0, flow_be32ofs = -1},
        ...
    

    这三个action的用法中都提到的field,必须能在全局变量mf_fields找到才能继续。

    除了上面说的三个action,还有如下常用的action用来修改报文内容

    mod_dl_src和mod_dl_dst: 修改报文源目的mac
    mod_nw_src和mod_nw_dst: 修改报文源目的ip
    mod_nw_tos: 修改ip头里的tos字段
    mod_tp_src和mod_tp_dst: 修改TCP,UDP等四层报文的源目的端口号
    

    源码分析

    命令行解析

    move action
    解析move action后,保存到结构体 struct ofpact_reg_move

    struct ofpact_reg_move {
        struct ofpact ofpact; //type为OFPACT_REG_MOVE
        struct mf_subfield src;
        struct mf_subfield dst;
    };
    
    parse_REG_MOVE
        struct ofpact_reg_move *move = ofpact_put_REG_MOVE(ofpacts);
        return nxm_parse_reg_move(move, arg);
            const char *full_s = s;
            char *error;
            //解析src field,subfield意思是可能只修改field的某些字段
            error = mf_parse_subfield__(&move->src, &s);
            if (error) {
                return error;
            }
            //必须包含 "->"
            if (strncmp(s, "->", 2)) {
                return xasprintf("%s: missing `->' following source", full_s);
            }
            s += 2;
            //解析 dst field
            error = mf_parse_subfield(&move->dst, s);
            if (error) {
                return error;
            }
            //src和dst field位宽必须一致
            if (move->src.n_bits != move->dst.n_bits) {
                return xasprintf("%s: source field is %d bits wide but destination is "
                                 "%d bits wide", full_s,
                                 move->src.n_bits, move->dst.n_bits);
            }
            return NULL;
    

    set_field和load都使用struct ofpact_set_field保存action

    struct ofpact_set_field {
        OFPACT_PADDED_MEMBERS(
            struct ofpact ofpact; //ofpact->type 为 OFPACT_SET_FIELD
            bool flow_has_vlan;   /* VLAN present at action validation time. */
            const struct mf_field *field; //目的 field
        );
        //要设置的值
        union mf_value value[];  /* Significant value bytes followed by
                                  * significant mask bytes. */  
    };
    
    //解析 set_field
    parse_SET_FIELD
        set_field_parse__(copy, port_map, ofpacts, usable_protocols);
            char *value;
            char *delim;
            char *key;
            const struct mf_field *mf;
            union mf_value sf_value, sf_mask;
    
            set_field_split_str(arg, &key, &value, &delim);
            mf = mf_from_name(key);
            delim[0] = '\0';
            mf_parse(mf, value, port_map, &sf_value, &sf_mask);
            mf_is_value_valid(mf, &sf_value)
            ofpact_put_set_field(ofpacts, mf, &sf_value, &sf_mask);
                struct ofpact_set_field *sf = ofpact_put_SET_FIELD(ofpacts);
                sf->field = field;
    
                /* Fill in the value and mask if given, otherwise put zeroes so that the
                 * caller may fill in the value and mask itself. */
                if (value) {
                    ofpbuf_put_uninit(ofpacts, 2 * field->n_bytes);
                    sf = ofpacts->header;
                    memcpy(sf->value, value, field->n_bytes);
                    if (mask) {
                        memcpy(ofpact_set_field_mask(sf), mask, field->n_bytes);
                    } else {
                        memset(ofpact_set_field_mask(sf), 0xff, field->n_bytes);
                    }
                } else {
                    ofpbuf_put_zeros(ofpacts, 2 * field->n_bytes);
                    sf = ofpacts->header;
                }
                /* Update length. */
                ofpact_finish_SET_FIELD(ofpacts, &sf);
    
    //解析 load
    ofpacts_parse__
        if (!strcasecmp(key, "load"))
            parse_reg_load(value, ofpacts);
                struct mf_subfield dst;
                char *key, *value_str;
                union mf_value value;
    
                //解析出 key value,key 为 "->" 后面的字符串,value 为 "->" 前面的字符串
                set_field_split_str(arg, &key, &value_str, NULL);
                //根据 key 查找 field 或者 subfield。subfield 表示 field 的其中某些位。
                mf_parse_subfield(&dst, key);
                
                parse_int_string(value_str, (uint8_t *)&value, dst.field->n_bytes, &key);
    
                struct ofpact_set_field *sf = ofpact_put_reg_load(ofpacts, dst.field, NULL, NULL);
    
                bitwise_copy(&value, dst.field->n_bytes, 0, sf->value, dst.field->n_bytes, dst.ofs, dst.n_bits);
                bitwise_one(ofpact_set_field_mask(sf), dst.field->n_bytes, dst.ofs, dst.n_bits);
    

    ovs-vswitchd接收openflow流表

    ovs-vswitchd接收到openflow流表,插入指定的table中,这里就不做分析了。

    报文匹配流表,执行action

    数据流首包查找datapath流表失败后,在slowpath中查找openflow流表,匹配成功后,将openflow流表及其action转换成datapath流表,并对报文执行action操作。

    慢速路径

    xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
        //后面查找到openflow流表后,其action可能会修改 xin->flow,
        //所以先保存 xin->flow 到 ctx->base_flow。
        //调用 xlate_commit_actions 时,比较 xin->flow 和 ctx->base_flow 差异即可得出有哪些action
        struct flow *flow = &xin->flow;
        struct xlate_ctx ctx = {
            .base_flow = *flow, //保存原始flow。
        };
        //查找流表
        rule_dpif_lookup_from_table
        
        do_xlate_actions(ofpacts, ofpacts_len, &ctx);
            struct flow_wildcards *wc = ctx->wc;
            struct flow *flow = &ctx->xin->flow;
            const struct ofpact *a;
    
            OFPACT_FOR_EACH (a, ofpacts, ofpacts_len)
                const struct ofpact_set_field *set_field;
                const struct mf_field *mf;
                
                switch (a->type) {
                case OFPACT_REG_MOVE:
                    xlate_ofpact_reg_move(ctx, ofpact_get_REG_MOVE(a));
                        mf_subfield_copy(&a->src, &a->dst, &ctx->xin->flow, ctx->wc);
                        xlate_report_subfield(ctx, &a->dst);
                    break;
                    
                case OFPACT_SET_FIELD:
                    set_field = ofpact_get_SET_FIELD(a);
                    mf = set_field->field;
    
                    /* Set the field only if the packet actually has it. */
                    //判断要设置的field在报文中是否存在。比如要修改mac地址,得保证报文有mac头
                    if (mf_are_prereqs_ok(mf, flow, wc)) {
                        mf_mask_field_masked(mf, ofpact_set_field_mask(set_field), wc);
                        //将要修改的字段保存到 flow
                        mf_set_flow_value_masked(mf, set_field->value, ofpact_set_field_mask(set_field), flow);
                    } else {
                        //如果不满足,则打印log即可
                        xlate_report(ctx, OFT_WARN,
                                     "unmet prerequisites for %s, set_field ignored",
                                     mf->name);
    
                    }
                    break;
                }
    
        /* Output only fully processed packets. */
        if (!ctx.freezing
            && xbridge->has_in_band
            && in_band_must_output_to_local_port(flow)
            && !actions_output_to_local_port(&ctx)) {
            //比较被openflow流表的action修改后的 flow 和报文原始 base_flow,
            //得出的差异就是openflow流表的action,需要
            //将其转换成 datapath action,并保存到 ctx->odp_actions。
            //后面流程会将原始 base_flow 和 ctx->odp_actions 添加到 datapath 流表中,
            //后续报文直接匹配 datapath 流表进行转发。
            compose_output_action(&ctx, OFPP_LOCAL, NULL);
                compose_output_action__(ctx, ofp_port, xr, true);
                    /* Commit accumulated flow updates before output. */
                    xlate_commit_actions(ctx);
                        commit_odp_actions(&ctx->xin->flow, &ctx->base_flow,
                                              ctx->odp_actions, ctx->wc,
                                              use_masked, ctx->pending_encap,
                                              ctx->encap_data);
    

    比较被openflow流表的action修改后的 flow 和报文原始 base_flow,得出 openflow action,转换成 datapath action。对于有些报文需要返回 slow_path_reason,表示此报文需要slowpath做特殊处理。

    enum slow_path_reason
    commit_odp_actions(const struct flow *flow, struct flow *base,
                       struct ofpbuf *odp_actions, struct flow_wildcards *wc,
                       bool use_masked, bool pending_encap,
                       struct ofpbuf *encap_data)
    {
        enum slow_path_reason slow1, slow2;
        bool mpls_done = false;
    
        commit_packet_type_change(flow, base, odp_actions, wc,
                                  pending_encap, encap_data);
        commit_set_ether_action(flow, base, odp_actions, wc, use_masked);
        /* Make packet a non-MPLS packet before committing L3/4 actions,
         * which would otherwise do nothing. */
        if (eth_type_mpls(base->dl_type) && !eth_type_mpls(flow->dl_type)) {
            commit_mpls_action(flow, base, odp_actions);
            mpls_done = true;
        }
        commit_set_nsh_action(flow, base, odp_actions, wc, use_masked);
        slow1 = commit_set_nw_action(flow, base, odp_actions, wc, use_masked);
        commit_set_port_action(flow, base, odp_actions, wc, use_masked);
        slow2 = commit_set_icmp_action(flow, base, odp_actions, wc);
        if (!mpls_done) {
            commit_mpls_action(flow, base, odp_actions);
        }
        commit_vlan_action(flow, base, odp_actions, wc);
        commit_set_priority_action(flow, base, odp_actions, wc, use_masked);
        commit_set_pkt_mark_action(flow, base, odp_actions, wc, use_masked);
    
        return slow1 ? slow1 : slow2;
    }
    

    以修改 ether 头为例,如果需要修改 ether 头,则会添加 action OVS_ACTION_ATTR_SET,其中又会嵌套 OVS_KEY_ATTR_ETHERNET。

    static void
    commit_set_ether_action(const struct flow *flow, struct flow *base_flow,
                            struct ofpbuf *odp_actions,
                            struct flow_wildcards *wc,
                            bool use_masked)
    {
        struct ovs_key_ethernet key, base, mask;
    
        if (flow->packet_type != htonl(PT_ETH)) {
            return;
        }
    
        get_ethernet_key(flow, &key);
        get_ethernet_key(base_flow, &base);
        get_ethernet_key(&wc->masks, &mask);
    
        if (commit(OVS_KEY_ATTR_ETHERNET, use_masked,
                   &key, &base, &mask, sizeof key, odp_actions))
            commit_set_action(odp_actions, attr, key, size);
                size_t offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_SET);
                nl_msg_put_unspec(odp_actions, key_type, key, key_size);
                nl_msg_end_nested(odp_actions, offset);
       {
            put_ethernet_key(&base, base_flow);
            put_ethernet_key(&mask, &wc->masks);
        }
    }
    

    参考

    https://man7.org/linux/man-pages/man7/ovs-fields.7.html
    https://man7.org/linux/man-pages/man7/ovs-actions.7.html

    相关文章

      网友评论

          本文标题:ovs set_field/load/move action

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