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触发的流程了。