OVS代码结构(by quqi99)
Posted quqi99
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OVS代码结构(by quqi99)相关的知识,希望对你有一定的参考价值。
作者:张华 发表于:2021-12-28
版权声明:可以任意转载,转载时请务必以超链接形式标明文章原始出处和作者信息及本版权声明
( http://blog.csdn.net/quqi99 )
问题
为了理解这个错误:
openvswitch: ovs-system: deferred action limit reached, drop recirc action
初步看来代码路径大概是:
ovs_dp_process_packet -> ovs_execute_actions -> process_deferred_actions -> do_execute_actions(OVS_ACTION_ATTR_RECIRC) -> (execute_recirc|sample|clone|execute_check_pkt_len) -> clone_execute -> add_deferred_actions -> action_fifo_put
但由于ovs的代码结构不清楚,所以对上面代码路径仍然不是很理解
为什么要理解这个错误,是因为下面这个VM can’t ping GW的问题:
VM(10.10.30.20, hosted in ecs4), GW chassis(ecs12)
在计算节点上抓包并分析, 从VM上得ping GW
openstack port list --server <vm>
tcpdump -enli tap<first-11-chars> -p `hostname`_<vm-1-tap>.pcap
tshark -r ecs4_xx.pcap ip.src==10.10.30.20 and icmp
在GW chassis(the node with the highest priority)上抓包
sudo ovn-nbctl lrp-list neutron-<router-uuid>
sudo ovn-nbctl lrp-get-gateway-chassis lrp-<ovn-port-uuid>
tcpdump -enli bond1 "(icmp or arp)" -w `hostname`_bond1.pcap
tshark -r ecs4_xx.pcap ip.src==10.10.30.20 and icmp
确实看到了intermittent pings
$ tshark -r ecs4_37552ee4-38.pcap ip.src==10.10.30.20 and icmp
254 74.664491 10.10.30.20 → 10.10.30.1 ICMP 98 Echo (ping) request id=0x17ab, seq=99/25344, ttl=64
267 75.679441 10.10.30.20 → 10.10.30.1 ICMP 98 Echo (ping) request id=0x17ab, seq=100/25600, ttl=64
268 75.679799 10.10.30.1 → 10.10.30.20 ICMP 98 Echo (ping) reply id=0x17ab, seq=100/25600, ttl=254 (request in 267)
ecs4上的sosreport看到了下列3种error:
$ grep -r 'deferred action limit reached' var/log/kern.log |tail -n1
Nov 8 13:14:30 ecs4 kernel: [9964180.307470] openvswitch: ovs-system: deferred action limit reached, drop recirc action
2021-11-10T00:00:31.476Z|147680|poll_loop|INFO|wakeup due to [POLLIN] on fd 3 (10.10.5.180:42162<->10.10.5.166:6642) at lib/stream-ssl.c:832 (101% CPU usage)
2021-11-10T00:01:07.194Z|147681|timeval|WARN|Unreasonably long 1110ms poll interval (1095ms user, 12ms system)
2021-11-10T00:01:07.194Z|147682|timeval|WARN|faults: 17299 minor, 0 major
2021-11-10T00:01:07.194Z|147683|coverage|INFO|Dropped 5 log messages in last 74 seconds (most recently, 35 seconds ago) due to excessive rate
$ var/log/ovn/ovn-controller.log.1.gz:2021-11-10T08:21:40.110Z|154925|ovsdb_idl|WARN|transaction error: "details":"Transaction causes multiple rows in \\"MAC_Binding\\" table to have identical values (lrp-fbf33f64-0cce-497d-a261-2d3d88e20b80 and \\"::\\") for index on columns \\"logical_port\\" and \\"ip\\". First row, with UUID 4e63d47d-791b-4cc1-ab3c-3d3ac29b5439, existed in the database before this transaction and was not modified by the transaction. Second row, with UUID 6d6281a0-a16e-4fbc-b8b2-da59038f22d5, was inserted by this transaction.","error":"constraint violation"
$ sudo ovn-nbctl show| egrep "^router |lrp-fbf33f64-0cce-497d-a261-2d3d88e20b80"| grep "port lrp" -B1
router 4307456d-3f8b-412c-a784-812f3e73fbfc (neutron-dbf7c13b-751a-41da-b504-09576617213e) (aka ansible-int)
port lrp-fbf33f64-0cce-497d-a261-2d3d88e20b80
$ sudo ovn-nbctl show 4307456d-3f8b-412c-a784-812f3e73fbfc
OVS代码结构
这篇文章不错: https://blog.csdn.net/nb_zsy/article/details/107893255
openflow controller下发流规则到ovsdb-server/ovs-vswitchd,再经netlink缓存到kernel datapath (可通过ovs-appctl dpctl/dump-flows type=ovs查看缓存的流规则)。以后kernel datapath直接根据这些cache流规则转发数据,如果不知道如何转发又得经netlink去查询(这个叫慢路径)。OVS的设计思路就是通过slow-path和fast-path的配合使用,完成网络数据的高效转发。同理,若网卡支持hw offload,可通过TC将流规则也cache在网卡硬件中以提升性能。
Datapath
从先openvswitch datapath内核模块开始,负责执行数据处理,也就是把从接收端口收到的数据包在流表中进行匹配,并执行匹配到的动作。一个datapath可以对应多个vport,一个vport类似物理交换机的端口概念。一个datapath关联一个flow table,一个flow table包含多个条目,每个条目包括两个内容:一个match/key和一个action.
数据处理时先进ovs_dp_process_packet,它根据mask和key进行匹配查找,没有找到flow, 需要发送到用户态进行慢速匹配。匹配的话有这些actions:
- OVS_ACTION_ATTR_OUTPUT:获取 port 号,调用 do_output()发送报文到该 port
- OVS_ACTION_ATTR_USERSPACE:调用 output_userspace()发送到用户态
- OVS_ACTION_ATTR_HASH:调用 execute_hash()获取 skb 的 hash 赋值到 ovs_flow_hash
- OVS_ACTION_ATTR_PUSH_VLAN:调用 push_vlan()增加 vlan 头
- OVS_ACTION_ATTR_RECIRC:在 action_fifos 全局数组中添加一个 deferred_action
- OVS_ACTION_ATTR_SET:调用 execute_set_action()设置相关参数
- OVS_ACTION_ATTR_SAMPLE:概率性的发送报文到用户态(与sflow相关)。
究竟何为OVS_ACTION_ATTR_RECIRC
对于openflow中的action ct会生成在datapath中用的action,可能包含两种action: OVS_ACTION_ATTR_CT和OVS_ACTION_ATTR_RECIRC,前者又包含了commit(OVS_CT_ATTR_FORCE_COMMIT),ct_mark(OVS_CT_ATTR_MARK), ct_label和nat(OVS_CT_ATTR_NAT)等信息,后者仅仅包含了recirc_id,用来重新注入datapath后查看到table id,即用来跳转到指定table执行。
见 - ovs conntrack及nat - https://www.jianshu.com/p/b52dc7496dbb
错误"deferred action limit reached, drop recirc action"是在说FIFO cache满了。
ovs upcall & dpif
datapath中根据key查不到流表就要触发upcall过程来查找openflow流表, 见 - https://www.codenong.com/cs109398201/
dpif是ovs-vswitched中的模块,upcall查到了openflow流表,然后dpif将它转化成精确流表并向kernel来下发流表, 见- ovs的upcall及ofproto-dpif处理细节 - https://blog.csdn.net/majieyue/article/details/52844738
do_xlate_actions会将openflow流表转化为精确流表. xlate_output_action根据不同的actions结果走不同的流程,比如当匹配流表的结果的normal时,进入xlate_normal, 在xlate_normal中就会做地址学习。
具体地,通过dp_packet生成udpif_keys,然后来查找datapath flow, struct dpif_upcall代表了一个报文的upcall, 一次处理UPCALL_MAX_BATCH个请求。
接收的数据放到struct dpif_upcall和stuct ofpbuf中,接收时,upcall_receive调用upcall_xlate,进而调用xlate_actions, rule_dpif_lookup_from_table查找到匹配的流表规则,进而生成actions
ct action
见 - ovs conntrack及nat - https://www.jianshu.com/p/b52dc7496dbb
ovs通过ct action实现contrack,kernel datapath会使用kernel的contrack来实现,对于userspace datapath来说由ovs本身来实现。ct action例子如下,见:https://blog.csdn.net/quqi99/article/details/51198098
#添加nat表项
ovs-ofctl add-flow br0 "table=0, priority=50, ct_state=-trk, tcp, in_port=veth_l0, actions=ct(commit,nat(src=10.1.1.240-10.2.2.2:2222-3333))"
//在一个ct里指定多次nat,只有最后一个nat生效,可参考do_xlate_actions中,ctx->ct_nat_action = ofpact_get_NAT(a)只有一个ctx->ct_nat_action
ovs-ofctl add-flow br0 "table=0, priority=50, ct_state=-trk, tcp, actions=ct(commit,nat(src=10.1.1.240-10.2.2.2:2222-3333), nat(dst=10.1.1.240-10.2.2.2:2222-3333)), veth_r0"
//可以通过指定多个ct,实现fullnat,即同时转换源目的ip。
//但是这两个ct必须指定不同的zone,否则只有第一个ct生效。因为在 handle_nat 中,判断只有zone不一样才会进行后续的nat操作
//错误方式,指定了src和dst nat,但是zone相同,只有前面的snat生效
ovs-ofctl add-flow br0 "table=0, priority=50, ct_state=-trk, tcp, actions=ct(commit,nat(src=10.1.1.240-10.2.2.2:2222-3333)), ct(commit,nat(dst=10.1.1.240-10.2.2.2:2222-3333)), veth_r0"
//正确方式,使用不同zone,指定fullnat
ovs-ofctl add-flow br0 "table=0, priority=50, ct_state=-trk, tcp, actions=ct(commit,zone=100, nat(src=10.1.1.240-10.2.2.2:2222-3333)), ct(commit, zone=200, nat(dst=10.1.1.240-10.2.2.2:2222-3333)), veth_r0"
ct_state连接状态,可能的值如下, 这些flag得结合"+“或者”-“来使用,”+“表示必须匹配,”-"表示必须不匹配。可以同时指定多个flag,比如: ct_state=+trk+new。
- new 通过ct action指定报文经过conntrack模块处理,不一定有commit。通常是数据流的第一个数据包
- est, 表示conntrack模块看到了报文双向数据流,一定是在commit 的conntrack后
- rel, 表示和已经存在的conntrack相关,比如icmp不可达消息或者ftp的数据流 rpl 表示反方向的报文
- inv, 无效的,表示conntrack模块没有正确识别到报文,比如L3/L4 protocol handler没有加载,或者L3/L4 protocol handler认为报文错误
- trk, 表示报文经过了conntrack模块处理,如果这个flag不设置,其他flag都不能被设置
- snat, 表示报文经过了snat,源ip或者port dnat 表示报文经过了dnat,目的ip或者port
ct支持的action如下:
- commit 只有执行了commit,才会在conntrack模块创建conntrack表项
- force, 强制删除已存在的conntrack表项 table 跳转到指定的table执行
- zone, 设置zone,隔离conntrack
- exec, 执行其他action,目前只支持设置ct_mark和ct_label,比如exec(set_field: 1->ct_mark)
- alg=<ftp/tftp> 指定alg类型,目前只支持ftp和tftp
- nat 指定ip和port
代码是如何实现的呢?例如下面这条流表:
ovs-ofctl add-flow br0 "table=0, priority=50, ct_state=-trk, tcp, in_port=veth_l0, actions=ct(commit,nat(src=10.1.1.240-10.2.2.2:2222-3333))"
通过ct_state匹配没经过conntrack处理的报文,一般刚被ovs接收的报文都能匹配到,执行的action是ct,其参数为commit和nat,表示需要创建conntrack表项,同时对报文做snat。
- 解析ct参数之后,struct ofpact_conntrack用来保存ct后面的参数,struct ofpact_nat保存ct的nat信息,struct ofpact_conntrack->actions用来保存action
- ovs-vswitchd的xlate模块将上面的ct流表信息转换为本地flow table, 所以slowpath需解析ct
action:
do_xlate_actions
const struct ofpact *a;
OFPACT_FOR_EACH (a, ofpacts, ofpacts_len)
switch (a->type)
//action为CT
case OFPACT_CT:
//ofpact_get_CT获取struct ofpact_conntrack及其后面嵌套的action
compose_conntrack_action(ctx, ofpact_get_CT(a));
ct_clear action & possible patch
这个possible patch是ct_clear action
https://patchwork.ozlabs.org/project/openvswitch/patch/162133301920.596425.5363293849951682159.stgit@wsfd-netdev64.ntdv.lab.eng.bos.redhat.com/
$ git tag --contains 355fef6f2c
v2.16.0
v2.16.1
v2.16.2
我们在使用:openvswitch-switch=2.13.3-0ubuntu0.20.04.1
有了它,The idea the packet nested recirculation path goes to deep and it will not follow the whole chain of actions because of it.
clone action
clone action引入自: https://mail.openvswitch.org/pipermail/ovs-dev/2016-November/325542.html
见:https://mail.openvswitch.org/pipermail/ovs-discuss/2017-October/045566.html
ovs有两种action,
- openflow action: 又分standard和extenstion, clone是extenstion action.
- datapath action: 类似openflow action, 但在datapath层
Reference
[1] https://blog.csdn.net/nb_zsy/article/details/107893255
[2] https://blog.csdn.net/quqi99/article/details/111831695
[3] https://xiaohutou.github.io/2018/06/01/ovs-code-note-1/
[4] OVS内核KEY值提取及匹配流表代码分析 - http://ry0117.com/2016/12/24/OVS%E5%86%85%E6%A0%B8KEY%E5%80%BC%E6%8F%90%E5%8F%96%E5%8F%8A%E5%8C%B9%E9%85%8D%E6%B5%81%E8%A1%A8%E4%BB%A3%E7%A0%81%E5%88%86%E6%9E%90/
以上是关于OVS代码结构(by quqi99)的主要内容,如果未能解决你的问题,请参考以下文章
如何实现OpenStack STT隧道(by quqi99)
set up ovn development env (by quqi99)