一、OpenvSwitch内核数据交互路径
通常,用户将通过“ ovs-ofctl” 实用程序与Open vSwitch内核数据路径交互,以将 OpenFlow 规则编程到“ ovs-vswitchd”中。然而,这并不是通过openvswitch内核模块转发数据包的唯一机制。使用“ ovs-dpctl”实用程序也可以使用额外的直接流编程接口将流添加到内核。
了解数据包如何通过内核数据路径非常重要。每当一个数据包到达时,发生的第一件事就是一个流表key填充了数据包中的所有元数据。该元数据包括以太网报头信息、ip 报头信息、arp 信息等,如果数据包通过这样的隧道到达,还可以与隧道协议信息分层。
一旦填充了流表关键数据,就会进行 skb 处理。流表key与现有内核中流缓存数据匹配(hash匹配)。如果找到匹配项,则执行与该流关联的操作。如果未找到匹配项,则数据包将被发送到 ovs-vswitchd 实用程序,称为上调用(upcall),或丢弃(在 ovs-vswitchd 未运行的情况下)。
用OpenvSwitch的说法,数据路径只是vport的集合。通常,一旦发生上调用,就会为数据路径填充流条目。数据包根据OpenFlow规则进行评估,随后的流表key添加操作被编程到内核空间中。
//ovs的主要组成模块:
#ovs-vswitchd:OVS守护进程是,OVS的核心部件,实现交换功能,和Linux内核兼容模块一起,实现基于流的交换(flow-based switching)。它和上层 controller 通信遵从 OPENFLOW 协议,它与 ovsdb-server 通信使用 OVSDB 协议,它和内核模块通过netlink通信,它支持多个独立的 datapath(网桥),它通过更改flow table 实现了绑定和VLAN等功能。
#ovsdb-server:轻量级的数据库服务,主要保存了整个OVS 的配置信息,包括接口啊,交换内容,VLAN啊等等。ovs-vswitchd 会根据数据库中的配置信息工作。它于 manager 和 ovs-vswitchd 交换信息使用了OVSDB(JSON-RPC)的方式。
#ovs-dpctl:一个工具,用来配置交换机内核模块,可以控制转发规则。
#ovs-vsctl:主要是获取或者更改ovs-vswitchd 的配置信息,此工具操作的时候会更新ovsdb-server 中的数据库。
#ovs-appctl:主要是向OVS 守护进程发送命令的,一般用不上。
#ovsdbmonitor:GUI 工具来显示ovsdb-server 中数据信息。
#ovs-controller:一个简单的OpenFlow 控制器
#ovs-ofctl:用来控制OVS 作为OpenFlow 交换机工作时候的流表内容。
二、action match syntax
一般来说actions之前都是match的部分,常用的一般有:
#in_port: switch的端口
#dl_src: 源mac地址
#dl_dst:目的mac地址
例如:dl_dst=01:00:00:00:00:00/01:00:00:00:00:00
匹配源和目的MAC地址。其中”/”后面的为掩码。
- 01:00:00:00:00:00 只匹配组播MAC (第一个字节LSB为1的MAC地址为MAC组播地址)
- fe:ff:ff:ff:ff:ff 匹配其他所有MAC,除了组播MAC
- ff:ff:ff:ff:ff:ff 完全匹配掩码前的MAC,和省掉掩码的效果一样
- 00:00:00:00:00:00 完全通配,相当于(dl_dst=*)
#dl_type:以太网协议类型 0x0806是arp packet 0x0800是ip packet
匹配L2(数据链路层) header中的协议类型,该字段描述L3的类型。有效值区间[0, 65535],可以是十进制数或者是以”0x”开头的十六进制数。比如:
dl_type=0x0800 匹配IP数据包
dl_type=0x0806 匹配ARP数据包
dl_type=0x8035 匹配RARP数据包
#nwproto 和 ipproto(要和 dl_type 字段配合使用)
当dl_type=0x0800或使用了关键字ip时,匹配IP头中的proto字段,取值区间[0, 255],比如为1时可以匹配ICMP数据包,为6时匹配TCP数据包。
当dl_type=0x86dd或使用了关键字ipv6是,匹配IPv6头中的proto字段,取值区间[0, 255],比如为58时匹配ICMPv6数据包,为6时匹配TCP数据包
当dl_type=0x0806或者使用了关键字arp时,匹配ARP opcode的低8位,ARP opcode大于255时,与等于0效果一样
当dl_type=0x8035或者使用了关键字rarp时,匹配ARP opcode的低8位, ARP opcode大于255时,与等于0效果一样
当dl_type使用了通配符或这除了0x0800, 0x0806, 0x8035以外的值,则nw_proto的值会被忽略
#nw_src:源IP
#nw_dst:目的ip
当使用了 dl_type=0x0800 或者关键字 ip 或 tcp 时,nw_src 和
nw_dst分配匹配IP头中的源IP地址和目的IP地址。其中netmask可以是255.255.255.0这样的(dotted quad)形式,也可以是数字 /24 这样的(CIDR)形式
#nw_proto
协议类型 ,注意和dl_type区分,同时也需要和dl_type一起使用,比如dl_type是ip(0x0800),那么nw_proto=1就表示icmp packet
#tp_src
tcp/udp源端口
#tp_dst
tcp/udp目的端口
#dl_vlan= <vlan>
匹配IEEE 802.1Q VLAN tag为 的数据包。 值应该在[0-4095]这个区间。为0xffff表示匹配没有VLAN tag的包
#dl_vlan_pcp
匹配IEEE 802.1Q Priority Code Point(PCP, 优先级代码点)为的数据包,改值取值区间为[0-7]。数字越大,表示优先级越高。可以用于QoS。
#nw_tos
匹配IP ToS字段或IPv6的traffic class字段取值为的数据包。取值区间[0, 255],需要注意的是最低2位会被忽略。当dl_type使用除0x800(IP)和0x86dd(IPv6)以外的数值时,该字段被忽略。
#nw_ttl
匹配IP和IPv6的TTL字段为的数据包。取值区间[0, 255]。当dl_type使用除0x800(IP)和0x86dd(IPv6)以为的数值时,该字段被忽略。
#icmp_type 和 icmp_code
icmp_type=<type>
icmp_code=<code>
当 dl_type 和 nw_proto 确定数据包为 ICMP 或 ICMPv6 时,匹配 ICMP type 和 code。取值区间都为[0, 255]。如果 dl_type 和 mw_proto 使用了其他值时,该字段忽略。
协议关键字 对应关系如下:
ip = dl_type=0x0800
ipv6 = dl_type=0x86dd
icmp = dl_type=0x0800,nw_proto=1
icmp6 = dl_type=0x86dd,nw_proto=58
tcp = dl_type=0x0800,nw_proto=6
tcp6 = dl_type=0x86dd,nw_proto=6
udp = dl_type=0x0800,nw_proto=17
udp6 = dl_type=0x86dd,nw_proto=17
sctp = dl_type=0x0800,nw_proto=132
sctp6 = dl_type=0x86dd,nw_proto=132
arp = dl_type=0x0806
rarp = dl_type=0x8035
mpls = dl_type=0x8847
mplsm = dl_type=0x8848
#cookie
cookie=<value>
或cookie=<value/mask>
cookie 字段可以用于以下命令 :add-flows和 mod-flows
三、actions字段
actions字段是flow的一部分,actions字段中可以有多个action,它们之间用逗号隔开,一个flow的完整语法如下:
<field>=<value>,[<field>=<value>]...,actions=<action>[,<action>...]
#output:port
将数据包输出到OpenFlow port
#group:<group_id>
将数据包输出到OpenFlow group
#normal
按照设备的常规L2/L3处理流程来处理数据包。这通常是OVS默认flow中的action。要注意的是,并不是所有的OpenFlow switch都支持这个action。
#reason=reason reason
可以是action,no_match,invalid_ttl
#id=controller-id
默认是0,特殊的controller会有一个16位的id
#flood
将数据包输出到所有物理端口,除了该数据包的输入口以及不可被flooding的端口
#all
将数据包输出到所有物理端口,除了该数据包的输入口
#local
将数据包输出到local port(与bridge同名的端口)
#in_port
将数据包输出到其输入口
#controller
controller(=…)
将数据包以”packet in”消息的形式发送到OpenFlow控制器。其中=键值对可以是:
max_len= 只将数据包的个字节发送到控制器。默认发送这个数据包。
reson= 指明”pakcet in”消息中的reason字段。默认reason为action,还可以是no_match, invalid_ttl。
id= 指明控制器id,16位整数。表示要发送给那个控制器。默认使用的id是0.
controller
controller[:nbytes]
分别是controller()和controller(max_len=)的简略写法。
#enqueue(,)
将数据包放到端口的队列中。其中必须是OpenFlow端口号或关键字(如”LOCAL”)。不同交换机支持的队列数不同,有些OpenFlow实现根本不支持队列。
#drop
丢掉该数据包。
#vlan
mod_vlan_vid:<vlan_vid>
修改数据包的VLAN id为。如果数据包没有VLAN tag则添加VLAN id为的VLAN tag。如果数据包VLAN id已经为,则将其VLAN 优先级priority设为0.
mod_vlan_pcp:<vlan_pcp>
修改数据包的VLAN 优先级priority为。如果数据包没有VLAN tag则添加VLAN priority为的VLAN tag。合法值区间为[0, 7],数字越大优先级越高。如果数据包VLAN priority已经为,则将其VLAN id设为0.
#strip_vlan
如果数据包有VLAN tag,则剥去VLAN tag
#datalink header mod
mod_dl_src:<mac>
mod_dl_dst:<mac>
设置数据包的源或目的MAC地址
#network header mod
od_nw_src:<ip>
mod_nw_dst:<ip>
设置数据包的源或目的IP地址
mod_nw_tos:<tos>
设置IPv4头ToS/DSCP或IPv6头traffic class field中DSCP比特位设置成,数值必须是4的倍数,且在[0, 255]区间。这个action并不会修改ToS中的低2位(2 LSB)。
mod_nw_ecn:<ecn>
设置IPv4头ToS或IPv6头traffic class field中ECN比特位为<ecn>,数值区间为[0, 3]。这个action并不会修改高6位(6 MSB)。
需要OpenFLow 1.1以上。
mod_nw_ttl:<ttl>
修改IPv4 TTL或IPv6 hop limit为,取值区间为[0, 255]。
需要OpenFlow 1.1以上。
#transport header mod
mod_tp_src:
mod_tp_dst:
将数据包的TCP/UDP/SCTP源或目的端口修改
四、ovs-vsctl 管理命令
//获取或者更改ovs-vswitchd的配置信息,此工具操作的时候会更新ovsdb-server 中的数据库。
– ovs-vsctl –V : Prints the current version of openvswitch.
– ovs-vsctl show : Prints a brief overview of the switch database configuration.
– ovs-vsctl list-br : Prints a list of configured bridges
– ovs-vsctl list-ports <bridge> : Prints a list of ports on a specific bridge.
– ovs-vsctl add-br <bridge> : Creates a bridge in the switch database.
– ovs-vsctl list interface : Prints a list of interfaces.
# ovs-vsctl list interface tap8934a9a8-f8
_uuid : 8974f75f-3002-4203-b36a-51ffced91e90
admin_state : up
bfd : {}
bfd_status : {}
cfm_fault : []
cfm_fault_status : []
cfm_flap_count : []
cfm_health : []
cfm_mpid : []
cfm_remote_mpids : []
cfm_remote_opstate : []
duplex : []
error : []
external_ids : {attached-mac="fa:16:3e:48:be:87", iface-id="8934a9a8-f846-4f66-a10b-22ec3d7981be", iface-status=active}
ifindex : 0
ingress_policing_burst: 0
ingress_policing_rate: 0
lacp_current : []
link_resets : 1
link_speed : []
link_state : up
lldp : {}
mac : []
mac_in_use : "fa:16:3e:48:be:87"
mtu : 1500
mtu_request : []
name : "tap8934a9a8-f8"
ofport : 23
ofport_request : []
options : {}
other_config : {}
statistics : {collisions=0, rx_bytes=7784390, rx_crc_err=0, rx_dropped=0, rx_errors=0, rx_frame_err=0, rx_over_err=0, rx_packets=164959, tx_bytes=9766348, tx_dropped=0, tx_errors=0, tx_packets=160305}
status : {driver_name=openvswitch}
type : internal
五、ovs-ofctl流操作命令
//用来控制OVS OpenFlow交换机工作时候的流表内容
//添加流表:
- ovs-ofctl add-flow <bridge> <flow>
- ovs-ofctl add-flow <bridge> <match-field> actions=all
- ovs-ofctl add-flow br-int in_port=3 actions=output:8
//添加VLAN tag
#ovs-ofctl add-flow br0 in_port=1,actions=mod_vlan_vid:10,output:2
//剥去VLAN tag
#ovs-ofctl add-flow br0 in_port=2,dl_vlan=100,actions=strip_vlan,output:1
#使用cookie添加一条 flow
#ovs-ofctl add-flow br0 cookie=0xf,tcp,tcp_dst=22,actions=mod_nw_tos:128,normal
//修改流表:
- ovs-ofctl mod-flows <bridge> <flow>
//删除指定匹配流表:
- ovs-ofctl del-flows <bridge> <flow>
#ovs-ofctl del-flows br-int cookie=0xf/-1,tcp,tcp_dst=22
#ovs-ofctl del-flows br-int cookie=0xf/-1
//Dump flow table
ovs-ofctl dump-tables br-int
//Dump br-int的所有flow:
ovs-ofctl dump-flows br-int
//Dump br-int上匹配xx的flow:
ovs-ofctl dump-flows br-int xx
//查看交换机端口详情(确认端口)
# ovs-ofctl show br-int
OFPT_FEATURES_REPLY (xid=0x2): dpid:00008a6744840049
n_tables:254, n_buffers:0
capabilities: FLOW_STATS TABLE_STATS PORT_STATS QUEUE_STATS ARP_MATCH_IP
actions: output enqueue set_vlan_vid set_vlan_pcp strip_vlan mod_dl_src mod_dl_dst mod_nw_src mod_nw_dst mod_nw_tos mod_tp_src mod_tp_dst
1(int-br-provider): addr:42:f7:71:ad:6f:0f
config: 0
state: 0
speed: 0 Mbps now, 0 Mbps max
2(tapd2e481eb-18): addr:fa:16:3e:f6:02:cb
config: 0
state: 0
speed: 0 Mbps now, 0 Mbps max
......
六、ovs-dpctl 操作流
//用来配置交换机内核模块,可以控制转发规则
// ovs-dpctl 不接受OpenFlow语法。相反,它使用一种特殊的语法来构建流表项规范。流键匹配规则由元数据的各个部分组成。我们将关注的是最常见的:以太网匹配规则、arp 匹配规则和 ip 匹配规则。甚至还有一个 encap()说明符,它允许窥视封装的数据包。每个流表key匹配规则可能要求下一层至少有一个“空匹配”。
//让我们创建双向 tcp 规则。我们需要匹配从端口2到端口1的数据包,tcp目标端口为8080。这将用于包含 SYN、ACK、FIN 或 RST 标志的 tcp 数据包。
# ovs-dpctl add-flow "in_port(2),eth(),eth_type(0x800),ipv4(proto=6),tcp(dst=8080),tcp_flags(0x1/0x1)"
# ovs-dpctl add-flow "in_port(2),eth(),eth_type(0x800),ipv4(proto=6),tcp(dst=8080),tcp_flags(0x2/0x2)"
# ovs-dpctl add-flow "in_port(2),eth(),eth_type(0x800),ipv4(proto=6),tcp(dst=8080),tcp_flags(0x4/0x4)"
# ovs-dpctl add-flow "in_port(2),eth(),eth_type(0x800),ipv4(proto=6),tcp(dst=8080),tcp_flags(0x10/0x10)"
//对于返回路径,我们需要来自端口 8080 的任何数据包:
# ovs-dpctl add-flow "in_port(1),eth(),eth_type(0x800),ipv4(proto=6),tcp(src=8080),tcp_flags(0x1/0x1)"
# ovs-dpctl add-flow "in_port(1),eth(),eth_type(0x800),ipv4(proto=6),tcp(src=8080),tcp_flags(0x2/0x2)"
# ovs-dpctl add-flow "in_port(1),eth(),eth_type(0x800),ipv4(proto=6),tcp(src=8080),tcp_flags(0x4/0x4)"
# ovs-dpctl add-flow "in_port(1),eth(),eth_type(0x800),ipv4(proto=6),tcp(src=8080),tcp_flags(0x10/0x10)"
七、ovs-appctl trace flow
//向OVS守护进程发送命令
# ovs-appctl ofproto/trace br-int arp,arp_spa=100.127.127.254
Flow: arp,in_port=ANY,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,arp_spa=100.127.127.254,arp_tpa=0.0.0.0,arp_op=0,arp_sha=00:00:00:00:00:00,arp_tha=00:00:00:00:00:00
bridge("br-int")
----------------
0. priority 1, cookie 0x52d47332fa25b1d1
resubmit(,104)
104. priority 0, cookie 0x52d47332fa25b1d1
resubmit(,59)
59. priority 0, cookie 0x52d47332fa25b1d1
goto_table:60
60. priority 5, cookie 0x52d47332fa25b1d1
resubmit(,101)
101. priority 0, cookie 0x52d47332fa25b1d1
NORMAL
-> no learned MAC for destination, flooding
bridge("br-provider")
---------------------
0. in_port=2, priority 2, cookie 0x5d1d2bf2cde950b8
drop
Final flow: unchanged
Megaflow: recirc_id=0,eth,arp,in_port=ANY,vlan_tci=0x0000/0x1fff,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00
Datapath actions: 3
八、查看生效的流表信息
//查看现在生效的流表命令:
# 查看默认的datapath类型的OVS桥的生效流表
ovs-appctl dpctl/dump-flows system@ovs-system
recirc_id(0),in_port(4),eth(src=fe:12:8c:6b:45:be,dst=76:06:f5:a5:89:80),eth_type(0x0800),ipv4(frag=no), packets:83887, bytes:8220204, used:0.487s, flags:SFPR., actions:7
recirc_id(0),in_port(3),eth(src=fe:12:8c:6b:45:be,dst=76:06:f5:a5:89:80),eth_type(0x0800),ipv4(frag=no), packets:83887, bytes:8220204, used:0.487s, flags:SFPR., actions:5
recirc_id(0),in_port(7),eth(src=76:06:f5:a5:89:80,dst=fe:12:8c:6b:45:be),eth_type(0x0800),ipv4(frag=no), packets:84009, bytes:8227760, used:0.487s, flags:SFPR., actions:4
recirc_id(0),in_port(5),eth(src=76:06:f5:a5:89:80,dst=fe:12:8c:6b:45:be),eth_type(0x0800),ipv4(frag=no), packets:84009, bytes:8227760, used:0.487s, flags:SFPR., actions:3
# 查看datapath类型为netdev的OVS桥的生效流表
ovs-appctl dpctl/dump-flows netdev@ovs-netdev
上述两条命令显示结果中的in_port(port_num)和actions:port_num,port_num值可能与使用ovs-ofctl命令查看到的不一致,
这是因为使用ovs-appctl命令显示的端口号是所有datapath类型的桥下接口的编号,而ovs-ofctl命令下的端口号是此OVS桥下的端口号。
查看完整的datapath类型的接口编号命令为:
# 查看当前OVS中的datapath类型
ovs-appctl dpctl/dump-dps
# 查看默认datapath类型的接口编号
ovs-appctl dpctl/show system@ovs-system
# 查看datapath=netdev类型的接口编号
ovs-appctl dpctl/show netdev@ovs-netdev