标签:vsr 接口 ble event 情况下 start 查询 stand goto
推荐看一下这篇文章 ,讲述了各个流表,我们这里着重讲流程和代码,对流表不再细说。
我们主要的关注点还是OVS-DPDK的流表转换,其实和OVS的转换差不多,只不过OVS的Datapath流表位于kernel,报文在Datapath找不到流表即通过netlink上传到Userspace,而OVS-DPDK则是Datapath流表依然位于Userspace,可以看做是一个缓存。查找不到的话直接继续调用其他接口查找Userspace的流表。
controller会根据网络情况给ovs下发流表,或者命令ovs-ofctl,属于Userspace的流表(ofproto classifier)。 当报文来的时候会先提取key值,然后在Datapath的流表(EMC或者microflow)进行查找匹配,查找到之后会进行action操作 如果没有查找到,就会转而继续查找Datapath的流表(也叫dpcls、TSS classifier、megaflow)。 dpcls有好多的子表,根据掩码来分类,需要挨个查找每个子表,如果查找到就会讲带掩码的流表转换成精确匹配的流表,然后匹配转发 如果匹配不到就会将key和报文传递给Userspace进行匹配 Userspace的查找会根据优先级和对每个table进行查找,然后执行相应的操作,最后匹配的所有流表会进行组合生成更简单的一些流表,比如table 0有n0条流表,一直到table 24有n24条流表,那么最终生成的dpcls流表可能有n1 x n2 x … x n24中可能性 将生成的流表转换安装到dpcls,然后转换安装到EMC 如果匹配不到的话会丢弃或者上报packet in给controller 边界点 EMC是以pmd为边界的,每个pmd都有自己的一套EMC dpcls是以端口边界的,每个端口都有自己的dpcls流表 ofproto classifier是以桥为边界的,每个桥都有自己的流表 流表下发 其实前面我们说到了三种存在形式的流表,这里流表下发只是下到了ofproto classifier了,其他的都是需要报文去触发去上一级拉取相应的流表。
流表发送 流表下发一般是两种方式:
controller,根据情况生成流表,通过openflow协议下发flow mod给ovs的Userspace流表。 命令ovs-ofctl,这个是根据命令情况生成流表,通过openflow协议下发flow mod给ovs的Userspace流表。 流表下发我们就先不去看了,因为我目前的原则是操作命令先不看,先看服务,controller后面会看ovn的,到时候单独来写。
流表接收 流表接收是指将命令行或者controller下发的流表接收,并且暂存。
服务启动 ovs-vswitchd.c
的路径为main-->bridge_run-->bridge_run__(针对每个桥运行ofproto)-->ofproto_run(信息量大,暂时忽略)-->handle_openflow
handle_openflow-->handle_openflow__
提取openflow协议的类型 简单介绍下部分openflow的协议类型ECHO是握手 FEATURES是同步版本和特性 CONFIG是同步配置 PACKET_OUT发包 PORT_MOD修改端口信息 FLOW_MOD修改流表,这个是最关键的 GROUP_MOD, TABLE_MOD, METER_MOD 最重要的OFPTYPE_FLOW_MOD
的操作为handle_flow_mod
主要是对流表的操作 handle_flow_mod
ofputil_decode_flow_mod
主要是将FLOW_MOD信息解析到ofputil_flow_mod
结构中handle_flow_mod__-->ofproto_flow_mod_init
主要是将上面ofputil_flow_mod
结构的数据解析到结构ofproto_flow_mod
中。handle_flow_mod__-->ofproto_flow_mod_start
支持了流表添加、删除、修改的操作,我们主要关注添加,即add_flow_start
ofproto_flow_mod_init
ofproto_flow_mod_init
涉及到数据结构的转换,我们先看下两个数据结构,然后看一下怎么转换的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 struct { struct ovs_list list_node ; struct match match ; int priority; ovs_be64 cookie; ovs_be64 cookie_mask; ovs_be64 new_cookie; bool modify_cookie; uint8_t table_id; uint16_t command; uint16_t idle_timeout; uint16_t hard_timeout; uint32_t buffer_id; ofp_port_t out_port; uint32_t out_group; enum ofputil_flow_mod_flags flags; uint16_t importance; struct ofpact *ofpacts ; size_t ofpacts_len; };
将ofputil_flow_mod
的数据转换为ofproto_flow_mod
的数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 struct ofproto_flow_mod { struct rule *temp_rule ; struct rule_criteria criteria ; struct cls_conjunction *conjs ; size_t n_conjs; uint16_t command; bool modify_cookie; bool modify_may_add_flow; bool modify_keep_counts; enum nx_flow_update_event event; ovs_version_t version; bool learn_adds_rule; struct rule_collection old_rules ; struct rule_collection new_rules ; };
首先直接拷贝过来的结构包括command、modify_cookie 然后根据操作的命令执行不同的函数,我先关注添加,即调用add_flow_init
没有指定table_id则指定table_id为0 根据指定的table_id找到数据结构oftable 调用cls_rule_init
和ofproto_rule_create
创建ofm->temp_rule
,这个接下来详细说下,主要是priority和match的填充 调用get_conjunctions
获取根据上面的ofpacts填充下面的conjs,这块还是不太懂,是和action相关的信息 rule创建
要看rule创建,我们首先了解一下rule的数据结构,然后看一下当前填充的priority和match
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 struct rule { struct ofproto *const ofproto ; const struct cls_rule cr ; const uint8_t table_id; enum rule_state state; struct ovs_refcount ref_count ; const ovs_be64 flow_cookie; struct hindex_node cookie_node OVS_GUARDED_BY (ofproto_mutex) ; enum ofputil_flow_mod_flags flags OVS_GUARDED; uint16_t hard_timeout OVS_GUARDED; uint16_t idle_timeout OVS_GUARDED; const uint16_t importance; uint8_t removed_reason; struct eviction_group *eviction_group OVS_GUARDED_BY (ofproto_mutex) ; struct heap_node evg_node OVS_GUARDED_BY (ofproto_mutex) ; const struct rule_actions * const actions ; struct ovs_list meter_list_node OVS_GUARDED_BY (ofproto_mutex) ; enum nx_flow_monitor_flags monitor_flags OVS_GUARDED_BY (ofproto_mutex) ; uint64_t add_seqno OVS_GUARDED_BY (ofproto_mutex) ; uint64_t modify_seqno OVS_GUARDED_BY (ofproto_mutex) ; struct ovs_list expirable OVS_GUARDED_BY (ofproto_mutex) ; long long int created OVS_GUARDED; long long int modified OVS_GUARDED; };
cls_rule_init
该函数主要是填充的ofm->temp_rule->cr
cls_rule_init__
是将priority填充minimatch_init
主要是将match填充,主要将struct match中的flow和wc分别填充到struct minimatch的flow和maskofproto_rule_create
该函数主要是创建ofm->temp_rule,并且填充一系列的内容,包括上面的new_cookie、idle_timeout、hard_timeout、flags、importance、ofpacts等
ofproto->ofproto_class->rule_alloc
申请rule空间rule->ofproto = ofproto指向自己的father 初始化引用计数ref_count 复制过来new_cookie、idle_timeout、hard_timeout、flags、importance 记录创建时间 调用rule_actions_create
来填充之前的ofpacts信息 调用rule_construct
进行一些信息的初始化 add_flow_start
classifier_find_rule_exactly
从流表指定的table(table_id)的classifier中通过掩码找到子表,在子表中进行匹配,必须找到匹配项,并且优先级和rule版本匹配,则找到流表,否则都是不匹配返回NULL没有匹配到规则,则需要判定规则总数是否超过最大值(UINT_MAX),超过需要删除一条流表。实际操作就是用新的流表替换掉旧的流表 如果找到匹配的规则,说明已有规则,则需要用新的流表替换掉旧的流表 replace_rule_start
主要操作就是新的流表替换旧的流表的操作,如果存在旧流表,则调用ofproto_rule_remove__
删除,然后调用ofproto_rule_insert__
和classifier_insert
添加流表。其中classifier_insert
主要是将rule->cr添加到table->cls中ofproto_rule_insert__
该函数主要是将rule插入到ofproto中去,上面有了rule的数据结构,下面我们看一下ofproto数据结构存储了什么信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 struct ofproto { struct hmap_node hmap_node ; const struct ofproto_class *ofproto_class ; char *type; char *name; uint64_t fallback_dpid; uint64_t datapath_id; bool forward_bpdu; char *mfr_desc; char *hw_desc; char *sw_desc; char *serial_desc; char *dp_desc; enum ofputil_frag_handling frag_handling; struct hmap ports ; struct shash port_by_name ; struct simap ofp_requests ; uint16_t alloc_port_no; uint16_t max_ports; struct hmap ofport_usage ; uint64_t change_seq; long long int eviction_group_timer; struct oftable *tables ; int n_tables; ovs_version_t tables_version; struct hindex cookies OVS_GUARDED_BY (ofproto_mutex) ; struct hmap learned_cookies OVS_GUARDED_BY (ofproto_mutex) ; struct ovs_list expirable OVS_GUARDED_BY (ofproto_mutex) ; struct ofputil_meter_features meter_features ; struct meter **meters ; struct connmgr *connmgr ; int min_mtu; struct cmap groups ; uint32_t n_groups[4 ] OVS_GUARDED; struct ofputil_group_features ogf ; OVSRCU_TYPE(struct tun_table *) metadata_tab; struct vl_mff_map vl_mff_map ; };
如果有超时时间的设置,调用ovs_list_insert
将rule->expirable添加到ofproto->expirable
中 调用cookies_insert
将rule->cookie_node
插入ofproto->cookies
eviction_group_add_rule
先不管如果有meter配置,调用meter_insert_rule
有group的话,调用ofproto_group_lookup
和group_add_rule
EMC查找 首先是报文接收,路径之前我们写过,pmd_thread_main-->dp_netdev_process_rxq_port-->netdev_rxq_recv-->netdev_dpdk_vhost_rxq_recv-->dp_netdev_input-->dp_netdev_input__
进行报文的处理。查找到就直接进行操作即可,如果查找不到的话就需要去dpcls进行查找了,找到后调用emc_insert
安装EMC流表。
key值提取 emc_processing-->miniflow_extract
会进行key值的提取。这块相对比较简单,我们就不看了,主要就是提取L2、L3、L4的报文协议头。
emc_processing
因为之前收取报文,一次最多NETDEV_MAX_BURST(32)个报文,所以是循环查表 miniflow_extract
主要是讲报文的信息提取到key->mf
dpif_netdev_packet_get_rss_hash
是获取rss计算的hash值,如果没有计算,则调用miniflow_hash_5tuple
计算出hash值emc_lookup
主要是在pmd的flowcache中查找表项,必须是hash值、key->mf、并且流表是alive的如果匹配,dp_netdev_queue_batches
主要是将报文添加到批处理中 如果不匹配,记录下不匹配的报文 循环持续到处理完所有的报文 dp_netdev_count_packet
主要是统计一下丢弃的报文、不匹配的报文、和EMC匹配的报文数dpcls查找 上面EMC查找匹配的报文会放在批处理里面,还会剩下不匹配的报文,接下来会在dpcls中查找。
fast_path_processing
dp_netdev_pmd_lookup_dpcls
根据报文入端口从pmd找出对应的classifier如果找不到classifier,则记录为miss 如果找到则继续调用dpcls_lookup
从各个子表找到合适的流表,只要有一个报文不匹配也记录为miss 如果都匹配则继续下面的操作,如果有不匹配的报文,尝试upcall的读锁 获取读锁失败,删掉不匹配的报文 获取读锁成功,则调用dp_netdev_pmd_lookup_flow
重新查一下,以防意外收获,查找到了就继续 如果依然没有查询到,则调用handle_packet_upcall
继续调用到ofproto classifier的流表查找。 接下来对报文检测已经匹配到流表的,调用emc_insert
将流表插入EMC中 最后调用dp_netdev_queue_batches
将报文加入批处理中 记录丢弃的报文、不匹配的报文、查找的报文和匹配掩码的报文 dpcls_lookup
比较复杂,主要是根据不同的掩码进行子表的区分,然后拿着报文分别去所有的子表用key和mask计算出hash,查看子表中有没有相应的node,如果有的话查看是否有hash冲突链,最终查看是否有匹配key值的表项。我们直接看一下代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 static bool dpcls_lookup(struct dpcls *cls, const struct netdev_flow_key keys[], struct dpcls_rule **rules, const size_t cnt, int *num_lookups_p) { typedef uint32_t map_type; struct dpcls_subtable *subtable ; map_type keys_map = TYPE_MAXIMUM(map_type); map_type found_map; uint32_t hashes[MAP_BITS]; const struct cmap_node *nodes [MAP_BITS ]; if (cnt != MAP_BITS) { keys_map >>= MAP_BITS - cnt; } memset (rules, 0 , cnt * sizeof *rules); int lookups_match = 0 , subtable_pos = 1 ; PVECTOR_FOR_EACH (subtable, &cls->subtables) { int i; ULLONG_FOR_EACH_1(i, keys_map) { hashes[i] = netdev_flow_key_hash_in_mask(&keys[i], &subtable->mask); } found_map = cmap_find_batch(&subtable->rules, keys_map, hashes, nodes); ULLONG_FOR_EACH_1(i, found_map) { struct dpcls_rule *rule ; CMAP_NODE_FOR_EACH (rule, cmap_node, nodes[i]) { if (OVS_LIKELY(dpcls_rule_matches_key(rule, &keys[i]))) { rules[i] = rule; subtable->hit_cnt++; lookups_match += subtable_pos; goto next; } } ULLONG_SET0(found_map, i); next: ; } keys_map &= ~found_map; if (!keys_map) { if (num_lookups_p) { *num_lookups_p = lookups_match; } return true ; } subtable_pos++; } if (num_lookups_p) { *num_lookups_p = lookups_match; } return false ; }
EMC流表安装 dpcls会查找到rules,然后rules转换成flow,最后调用emc_insert
将流表插入到EMC中。
emc_insert
根据key->hash找到hash桶,并且进行轮询 查看是否有匹配的key值,有的话调用emc_change_entry
修改流表。 如果没有匹配的就会根据算法记录一个entry,用来替代 循环完毕之后,调用emc_change_entry
替代之前不用的流表 emc_change_entry
操作很简单,就是赋值netdev_flow_key和dp_netdev_flow
ofproto classifier查找 handle_packet_upcall
miniflow_expand
讲key->mf解析到match.flowdpif_flow_hash
根据key值计算出hashdp_netdev_upcall
是进一步调用去ofproto classifier查表的接口,如果失败则删除报文dp_netdev_execute_actions
可能是直接执行action,后期需要看看为什么不能放入批处理,现在还不明白dp_netdev_pmd_lookup_flow
需要重新查找dpcls,没有查找到则调用dp_netdev_flow_add
添加流表emc_insert
讲dpcls的流表插入EMC中dp_netdev_upcall-->upcall_cb
upcall_receive
主要是将一堆信息解析到upcall中process_upcall
根据upcall的类型MISS_UPCALL确定调用函数upcall_xlate
upcall_xlate
xlate_in_init
主要是将upcall的数据转给xlate_inxlate_actions
主要是进行流表查找xlate_actions
调用xbridge_lookup
查找对应的xbridge信息 根据当前掌握的一堆信息生成一个结构xlate_ctx
xlate_wc_init
主要是初始化通配符的一些已知的项rule_dpif_lookup_from_table
会查找指定table的流表,默认是table 0,用一个循环去遍历每一个table,然后知道找到匹配的ruledo_xlate_actions
主要是执行所有的action,轮询所有的action,并且根据具体的情况进行相应的操作。tun_metadata_to_geneve_udpif_mask
给geneve封装metadatarule_dpif_lookup_from_table
如果报文分片,默认是设置源目的端口都设置为0,其他情况下丢弃报文。 遍历所有的table,每次都会调用rule_dpif_lookup_in_table
去查找rule,如果找到最终找到之后返回,找不到的话就会去下一个table找。 如果遍历完成都找不到,则返回miss_rule do_xlate_actions
根据查找到的rule,遍历所有的action,支持的有OFPACT_OUTPUT、OFPACT_GROUP、OFPACT_CONTROLLER、OFPACT_ENQUEUE、OFPACT_SET_VLAN_VID、OFPACT_SET_VLAN_PCP、OFPACT_STRIP_VLAN、OFPACT_PUSH_VLAN、OFPACT_SET_ETH_SRC、OFPACT_SET_ETH_DST、OFPACT_SET_IPV4_SRC、OFPACT_SET_IPV4_DST、OFPACT_SET_IP_DSCP、OFPACT_SET_IP_ECN、OFPACT_SET_IP_TTL、OFPACT_SET_L4_SRC_PORT、OFPACT_SET_L4_DST_PORT、OFPACT_RESUBMIT、OFPACT_SET_TUNNEL、OFPACT_SET_QUEUE、OFPACT_POP_QUEUE、OFPACT_REG_MOVE、OFPACT_SET_FIELD、OFPACT_STACK_PUSH、OFPACT_STACK_POP、OFPACT_PUSH_MPLS、OFPACT_POP_MPLS、OFPACT_SET_MPLS_LABEL、OFPACT_SET_MPLS_TC、OFPACT_SET_MPLS_TTL、OFPACT_DEC_MPLS_TTL、OFPACT_DEC_TTL、OFPACT_NOTE、OFPACT_MULTIPATH、OFPACT_BUNDLE、OFPACT_OUTPUT_REG、OFPACT_OUTPUT_TRUNC、OFPACT_LEARN、OFPACT_CONJUNCTION、OFPACT_EXIT、OFPACT_UNROLL_XLATE、OFPACT_FIN_TIMEOUT、OFPACT_CLEAR_ACTIONS、OFPACT_WRITE_ACTIONS、OFPACT_WRITE_METADATA、OFPACT_METER、OFPACT_GOTO_TABLE、OFPACT_SAMPLE、OFPACT_CLONE、OFPACT_CT、OFPACT_CT_CLEAR、OFPACT_NAT、OFPACT_DEBUG_RECIRC,因为action太多,我们先介绍几个常用的 OFPACT_OUTPUT
,xlate_output_action
会根据端口情况进行一些操作,这块不细看了OFPACT_CONTROLLER
,execute_controller_action
生成一个packet_in报文,然后发送OFPACT_SET_ETH_SRC
、OFPACT_SET_ETH_DST
、OFPACT_SET_IPV4_SRC
、OFPACT_SET_IPV4_DST
、OFPACT_SET_IP_DSCP
、OFPACT_SET_IP_ECN
、OFPACT_SET_IP_TTL
、OFPACT_SET_L4_SRC_PORT
、OFPACT_SET_L4_DST_PORT
,修改源目的mac、IP以及DSCP、ECN、TTL和L4的源目的端口OFPACT_RESUBMIT
,xlate_ofpact_resubmit
会继续查找指定的table的流表OFPACT_SET_TUNNEL
,设置tunnel idOFPACT_CT
,compose_conntrack_action
执行完ct的设置之后回调do_xlate_actions
执行其他的actionrule_dpif_lookup_from_table-->rule_dpif_lookup_in_table-->classifier_lookup-->classifier_lookup__
遍历所有子表,然后调用find_match_wc
根据流表和掩码计算hash,然后进行对子表的各个rule进行匹配比较、 如果是严格匹配的话就直接返回,不是严格匹配的话还有一系列操作,暂时先不写了。 dpcls 流表安装 handle_packet_upcall-->dp_netdev_flow_add-->dpcls_insert
主要是根据掩码找到相应的子表,然后插入当前的流表
原文:大专栏 OpenvSwitch 流表转换
OpenvSwitch 流表转换
标签:vsr 接口 ble event 情况下 start 查询 stand goto
原文地址:https://www.cnblogs.com/petewell/p/11614991.html