searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

ovs flow_mode的触发机制

2023-08-29 02:09:37
178
0

revalidate线程:

revalidate线程一直在不停的loop各个生成的flow表项。
获取到流表后。
ukey_acquire 根据flow的ufid查找到对应的ukey 。
 
1. revalidate_ukey 验证'ukey'的数据路径acion是否正确,并推送'stats'。
  • 检查ukey的reval_seq与udpif->reval_seq是否相等。
  • 如果不相等,需要重新验证合法性。
    • ukey是否需要验证
    • revalidate_ukey__ 判断流表的状态
 1.1 revalidate_ukey__验证ukey是否是合法的
  • xlate_ukey 将'key'转换为一个流,同时填充'ctx'。
    • odp_flow_key_to_flow 将ukey的'key'中的'key_len'字节的OVS_KEY_ATTR_* 属性转换为'ctx的flow'。返回一个ODP_FIT_*,指示'key'与我们对流键应包含的期望程度。
      • parse_flow_nlattrs 将key中的attr指针存储到attr数组中,同时记录动作的type到present_attrs形成metadata。
      • 根据present_attrs原数据将attr数组中的具体值填充到flow中。对于OVS_KEY_ATTR_TUNNEL,填充flow->tunnel表
 示例代码                 
    /* Parse attributes. */
    if (!parse_flow_nlattrs(key, key_len, attrs, &present_attrs,
                            &out_of_range_attr, errorp)) {
        goto exit;
    }
    expected_attrs = 0;

    /* For OVS to be backward compatible with newer datapath implementations,
     * we should ignore out of range attributes. */
    if (out_of_range_attr) {
        VLOG_DBG("Flow key decode found unknown OVS_KEY_ATTR, %d",
                 out_of_range_attr);
        out_of_range_attr = 0;
    }

    /* Metadata. */
    if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_RECIRC_ID)) {
        flow->recirc_id = nl_attr_get_u32(attrs[OVS_KEY_ATTR_RECIRC_ID]);
        expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_RECIRC_ID;
    } else if (is_mask) {
        /* Always exact match recirc_id if it is not specified. */
        flow->recirc_id = UINT32_MAX;
    }

    if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_DP_HASH)) {
        flow->dp_hash = nl_attr_get_u32(attrs[OVS_KEY_ATTR_DP_HASH]);
        expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_DP_HASH;
    }
    ...
        if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_TUNNEL)) {
        enum odp_key_fitness res;

        res = odp_tun_key_from_attr__(attrs[OVS_KEY_ATTR_TUNNEL], is_mask,
                                      &flow->tunnel, errorp);
        if (res == ODP_FIT_ERROR) {
            goto exit;
        } else if (res == ODP_FIT_PERFECT) {
            expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_TUNNEL;
        }
    }
    • xlate_lookup 根据odp_flow_key_to_flow生成的flow查找。
      • xlate_lookup_ofproto_
        • 首先根据flow->recirc_id查找recirc_id_node(recicle_id为非0)
        • 如果recirc_id_node->state.metadata.in_port非空,则继续查找xcfgp rcu数组
          • xport_lookup_by_uuid,根据recirc_id_node->state.xport_uuid在xcfgp.xcfg->xports_uuid查找。
          • 找到xport后,判断合法性。
        • 如果recirc_id_node->state.metadata.in_port为空
          • OFPP_NONE和OFPP_CONTROLLER不是真正的端口。它们表示通过OpenFlow的"packet-out"从控制器发送的数据包。正确的做法是只找到ofproto,而没有xport,这是可以接受的。OFPP_NONE 也可以表示由于链路聚合(bond)导致的数据包循环。直接返回bridge->ofproto.
        • xport_lookup 再次查询xport。(不管recicle_id是否为0)查询失败返回NULL。
        • 成功返回xport->xbridge->ofproto
      • 记录ofprotop,以及ipfix,sflow,netflow的状态。
    • xlate_in_init 填充xin结构体
    • xlate_actions:将flow翻译为datapath actions
      • 如果原始流量没有通过隧道进入,则它不会设置 FLOW_TNL_F_UDPIF 标志。然而,我们仍然需要有一个元数据表,以防我们生成隧道动作。
flow->tunnel.metadata.tab = ofproto_get_tun_tab(&ctx.xbridge->ofproto->up);
ctx.wc->masks.tunnel.metadata.tab = flow->tunnel.metadata.tab;
 
      • 获取数据包的直接输入端口。(如果 xin->frozen_state 为真,flow->in_port 是数据包的最终输入端口。
struct xport *in_port = get_ofp_port(xbridge,ctx.base_flow.in_port.ofp_port);
      • 如果数据包来自 L3 端口并且不是 L2 包,则向非 L2 数据包添加虚拟以太网头部。这样,所有数据包都将被视为 L2 数据包进行查找操作。dl_type 已经从 packet_type 中设置好了。
if (flow->packet_type != htonl(PT_ETH) && in_port &&
        in_port->pt_mode == NETDEV_PT_LEGACY_L3 && ctx.table_id == 0) {
        /* Add dummy Ethernet header to non-L2 packet if it's coming from a
         * L3 port. So all packets will be L2 packets for lookup.
         * The dl_type has already been set from the packet_type. */
        flow->packet_type = htonl(PT_ETH);
        flow->dl_src = eth_addr_zero;
        flow->dl_dst = eth_addr_zero;
        ctx.pending_encap = true;
    }                  
      • 以上的步骤都是填充flow
      • 进入重头戏:寻找openflow流表(具体细节后续openflow存储时仔细描述)
for (next_id = *table_id;
         next_id < ofproto->up.n_tables;
         next_id++, next_id += (next_id == TBL_INTERNAL))
    {
        *table_id = next_id;
        rule = rule_dpif_lookup_in_table(ofproto, version, next_id, flow, wc);
        if (stats) {
            struct oftable *tbl = &ofproto->up.tables[next_id];
            unsigned long orig;

            atomic_add_relaxed(rule ? &tbl->n_matched : &tbl->n_missed,
                               stats->n_packets, &orig);
        }
        if (xcache) {
            if (ofproto_try_ref(&ofproto->up)) {
                struct xc_entry *entry;

                entry = xlate_cache_add_entry(xcache, XC_TABLE);
                entry->table.ofproto = ofproto;
                entry->table.id = next_id;
                entry->table.match = (rule != NULL);
            }
        }
        if (rule) {
            goto out;   /* Match. */
        }
        if (honor_table_miss) {
            miss_config = ofproto_table_get_miss_config(&ofproto->up,
                                                        *table_id);
            if (miss_config == OFPUTIL_TABLE_MISS_CONTINUE) {
                continue;
            }
        }
        break;
    }
从ofproto->up.tables[table_id].cls取cls。
static struct rule_dpif *
rule_dpif_lookup_in_table(struct ofproto_dpif *ofproto, ovs_version_t version,
                          uint8_t table_id, struct flow *flow,
                          struct flow_wildcards *wc)
{
    struct classifier *cls = &ofproto->up.tables[table_id].cls;
    return rule_dpif_cast(rule_from_cls_rule(classifier_lookup(cls, version,
                                                               flow, wc)));
}
      • 在上述步骤获取ctx.rule后,获取actions
      • else if (ctx.rule) {
                        const struct rule_actions *actions
                            = rule_get_actions(&ctx.rule->up);
                        ofpacts = actions->ofpacts;
                        ofpacts_len = actions->ofpacts_len;
                        ctx.rule_cookie = ctx.rule->up.flow_cookie;
                    } else {
                        OVS_NOT_REACHED();
                    }
      • do_xlate_actions根据上述步骤获取的ctx.rule,查到到action.获取所有的action.并填充到ctx.odp_actions中。
        • 遍历所有action动作根据动作类型拼接动作。填充ctx.odp_actions和flow表项。
        • 最终调用out_put动作时拼接动作
          • compose_output_action
  • 根据获取的odp_action与原来的ukey->actions做对比.如果相等则保持,如果不等则返回UKEY_MODIFY.
  • if (!ofpbuf_equal(odp_actions,
                      ovsrcu_get(struct ofpbuf *, &ukey->actions))) {
        /* The datapath mask was OK, but the actions seem to have changed.
         * Let's modify it in place. */
        result = UKEY_MODIFY;
        /* Transfer recirc action ID references to the caller. */
        recirc_refs_swap(recircs, &xoutp->recircs);
        goto exit;
    }
    
    result = UKEY_KEEP;
至此我们从流程清楚MODIFY触发的流程了。
0条评论
0 / 1000
吴****华
4文章数
0粉丝数
吴****华
4 文章 | 0 粉丝
吴****华
4文章数
0粉丝数
吴****华
4 文章 | 0 粉丝
原创

ovs flow_mode的触发机制

2023-08-29 02:09:37
178
0

revalidate线程:

revalidate线程一直在不停的loop各个生成的flow表项。
获取到流表后。
ukey_acquire 根据flow的ufid查找到对应的ukey 。
 
1. revalidate_ukey 验证'ukey'的数据路径acion是否正确,并推送'stats'。
  • 检查ukey的reval_seq与udpif->reval_seq是否相等。
  • 如果不相等,需要重新验证合法性。
    • ukey是否需要验证
    • revalidate_ukey__ 判断流表的状态
 1.1 revalidate_ukey__验证ukey是否是合法的
  • xlate_ukey 将'key'转换为一个流,同时填充'ctx'。
    • odp_flow_key_to_flow 将ukey的'key'中的'key_len'字节的OVS_KEY_ATTR_* 属性转换为'ctx的flow'。返回一个ODP_FIT_*,指示'key'与我们对流键应包含的期望程度。
      • parse_flow_nlattrs 将key中的attr指针存储到attr数组中,同时记录动作的type到present_attrs形成metadata。
      • 根据present_attrs原数据将attr数组中的具体值填充到flow中。对于OVS_KEY_ATTR_TUNNEL,填充flow->tunnel表
 示例代码                 
    /* Parse attributes. */
    if (!parse_flow_nlattrs(key, key_len, attrs, &present_attrs,
                            &out_of_range_attr, errorp)) {
        goto exit;
    }
    expected_attrs = 0;

    /* For OVS to be backward compatible with newer datapath implementations,
     * we should ignore out of range attributes. */
    if (out_of_range_attr) {
        VLOG_DBG("Flow key decode found unknown OVS_KEY_ATTR, %d",
                 out_of_range_attr);
        out_of_range_attr = 0;
    }

    /* Metadata. */
    if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_RECIRC_ID)) {
        flow->recirc_id = nl_attr_get_u32(attrs[OVS_KEY_ATTR_RECIRC_ID]);
        expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_RECIRC_ID;
    } else if (is_mask) {
        /* Always exact match recirc_id if it is not specified. */
        flow->recirc_id = UINT32_MAX;
    }

    if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_DP_HASH)) {
        flow->dp_hash = nl_attr_get_u32(attrs[OVS_KEY_ATTR_DP_HASH]);
        expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_DP_HASH;
    }
    ...
        if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_TUNNEL)) {
        enum odp_key_fitness res;

        res = odp_tun_key_from_attr__(attrs[OVS_KEY_ATTR_TUNNEL], is_mask,
                                      &flow->tunnel, errorp);
        if (res == ODP_FIT_ERROR) {
            goto exit;
        } else if (res == ODP_FIT_PERFECT) {
            expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_TUNNEL;
        }
    }
    • xlate_lookup 根据odp_flow_key_to_flow生成的flow查找。
      • xlate_lookup_ofproto_
        • 首先根据flow->recirc_id查找recirc_id_node(recicle_id为非0)
        • 如果recirc_id_node->state.metadata.in_port非空,则继续查找xcfgp rcu数组
          • xport_lookup_by_uuid,根据recirc_id_node->state.xport_uuid在xcfgp.xcfg->xports_uuid查找。
          • 找到xport后,判断合法性。
        • 如果recirc_id_node->state.metadata.in_port为空
          • OFPP_NONE和OFPP_CONTROLLER不是真正的端口。它们表示通过OpenFlow的"packet-out"从控制器发送的数据包。正确的做法是只找到ofproto,而没有xport,这是可以接受的。OFPP_NONE 也可以表示由于链路聚合(bond)导致的数据包循环。直接返回bridge->ofproto.
        • xport_lookup 再次查询xport。(不管recicle_id是否为0)查询失败返回NULL。
        • 成功返回xport->xbridge->ofproto
      • 记录ofprotop,以及ipfix,sflow,netflow的状态。
    • xlate_in_init 填充xin结构体
    • xlate_actions:将flow翻译为datapath actions
      • 如果原始流量没有通过隧道进入,则它不会设置 FLOW_TNL_F_UDPIF 标志。然而,我们仍然需要有一个元数据表,以防我们生成隧道动作。
flow->tunnel.metadata.tab = ofproto_get_tun_tab(&ctx.xbridge->ofproto->up);
ctx.wc->masks.tunnel.metadata.tab = flow->tunnel.metadata.tab;
 
      • 获取数据包的直接输入端口。(如果 xin->frozen_state 为真,flow->in_port 是数据包的最终输入端口。
struct xport *in_port = get_ofp_port(xbridge,ctx.base_flow.in_port.ofp_port);
      • 如果数据包来自 L3 端口并且不是 L2 包,则向非 L2 数据包添加虚拟以太网头部。这样,所有数据包都将被视为 L2 数据包进行查找操作。dl_type 已经从 packet_type 中设置好了。
if (flow->packet_type != htonl(PT_ETH) && in_port &&
        in_port->pt_mode == NETDEV_PT_LEGACY_L3 && ctx.table_id == 0) {
        /* Add dummy Ethernet header to non-L2 packet if it's coming from a
         * L3 port. So all packets will be L2 packets for lookup.
         * The dl_type has already been set from the packet_type. */
        flow->packet_type = htonl(PT_ETH);
        flow->dl_src = eth_addr_zero;
        flow->dl_dst = eth_addr_zero;
        ctx.pending_encap = true;
    }                  
      • 以上的步骤都是填充flow
      • 进入重头戏:寻找openflow流表(具体细节后续openflow存储时仔细描述)
for (next_id = *table_id;
         next_id < ofproto->up.n_tables;
         next_id++, next_id += (next_id == TBL_INTERNAL))
    {
        *table_id = next_id;
        rule = rule_dpif_lookup_in_table(ofproto, version, next_id, flow, wc);
        if (stats) {
            struct oftable *tbl = &ofproto->up.tables[next_id];
            unsigned long orig;

            atomic_add_relaxed(rule ? &tbl->n_matched : &tbl->n_missed,
                               stats->n_packets, &orig);
        }
        if (xcache) {
            if (ofproto_try_ref(&ofproto->up)) {
                struct xc_entry *entry;

                entry = xlate_cache_add_entry(xcache, XC_TABLE);
                entry->table.ofproto = ofproto;
                entry->table.id = next_id;
                entry->table.match = (rule != NULL);
            }
        }
        if (rule) {
            goto out;   /* Match. */
        }
        if (honor_table_miss) {
            miss_config = ofproto_table_get_miss_config(&ofproto->up,
                                                        *table_id);
            if (miss_config == OFPUTIL_TABLE_MISS_CONTINUE) {
                continue;
            }
        }
        break;
    }
从ofproto->up.tables[table_id].cls取cls。
static struct rule_dpif *
rule_dpif_lookup_in_table(struct ofproto_dpif *ofproto, ovs_version_t version,
                          uint8_t table_id, struct flow *flow,
                          struct flow_wildcards *wc)
{
    struct classifier *cls = &ofproto->up.tables[table_id].cls;
    return rule_dpif_cast(rule_from_cls_rule(classifier_lookup(cls, version,
                                                               flow, wc)));
}
      • 在上述步骤获取ctx.rule后,获取actions
      • else if (ctx.rule) {
                        const struct rule_actions *actions
                            = rule_get_actions(&ctx.rule->up);
                        ofpacts = actions->ofpacts;
                        ofpacts_len = actions->ofpacts_len;
                        ctx.rule_cookie = ctx.rule->up.flow_cookie;
                    } else {
                        OVS_NOT_REACHED();
                    }
      • do_xlate_actions根据上述步骤获取的ctx.rule,查到到action.获取所有的action.并填充到ctx.odp_actions中。
        • 遍历所有action动作根据动作类型拼接动作。填充ctx.odp_actions和flow表项。
        • 最终调用out_put动作时拼接动作
          • compose_output_action
  • 根据获取的odp_action与原来的ukey->actions做对比.如果相等则保持,如果不等则返回UKEY_MODIFY.
  • if (!ofpbuf_equal(odp_actions,
                      ovsrcu_get(struct ofpbuf *, &ukey->actions))) {
        /* The datapath mask was OK, but the actions seem to have changed.
         * Let's modify it in place. */
        result = UKEY_MODIFY;
        /* Transfer recirc action ID references to the caller. */
        recirc_refs_swap(recircs, &xoutp->recircs);
        goto exit;
    }
    
    result = UKEY_KEEP;
至此我们从流程清楚MODIFY触发的流程了。
文章来自个人专栏
文章 | 订阅
0条评论
0 / 1000
请输入你的评论
1
0