Open vSwitch
Open vSwitch 是一种产品质量的多层虚拟交换机,使用开源 Apache 2.0许可证授权。它旨在通过编程扩展实现大规模网络自动化,同时仍然支持标准的管理接口和协议(例如 NetFlow、 sFlow、 IPFIX、 RSPAN、 CLI、 LACP、802.1 ag)。
-
通过 NetFlow、sFlow(R)、IPFIX、SPAN、RSPAN 和 GRE 隧道镜像查看 VM 间通信
-
LACP (IEEE 802.1AX-2008)
-
具有中继的标准 802.1Q VLAN 模型
-
组播侦听
-
IETF 自动附加 SPBM 和基本所需的 LLDP 支持
-
BFD 和 802.1ag 链路监控
-
STP (IEEE 802.1D-1998) 和 RSTP (IEEE 802.1D-2004)
-
细粒度的 QoS 控制
-
支持 HFSC qdisc
-
每个虚拟机接口流量监管
-
具有源 MAC 负载平衡、主动备份和 L4 散列的 NIC 绑定
-
OpenFlow 协议支持(包括许多虚拟化扩展)
- IPv6 支持
-
多种隧道协议(GRE、VXLAN、STT 和 Geneve,支持 IPsec)
-
具有 C 和 Python 绑定的远程配置协议
-
内核和用户空间转发引擎选项
-
带有流缓存引擎的多表转发管道
-
转发层抽象,便于移植到新的软件和硬件平台
介绍来自Open vSwitch 官方文档,可能会有更新,最新文档可以通过官网查看。
ovs architecture
ovs的架构如上图所示,主要由内核datapath和用户空间的vswitchd、ovsdb组成
-
datapath:是负责数据交换的内核模块,从网卡读取数据,并快速匹配flowtable中的流表项,成功的直接转发,失败的上交vswtichd处理。它在初始化和port binding的时候注册hook函数,把端口保温处理接管到内核模块,。。。。。 属于快速转发平面,主要负责流表匹配、保温修改、隧道封装、转发或者上送。并且维护底层转发表。在原始ovs中,报文首先经过该组件完成报文解析和封装、转发规则匹配,若找到转发规则不再经过用户空间,直接转发。否则转交用户空间的ovs-vswitchd组件进行处理。ovs-vswitchd组件与openvswitch.ko组件之间采用netlink执行进程间的通信。netlink是一种进程间通信机制,可用于处理用户态和内核态的通信。 -
vswitchd:一个守护进程,是ovs的管理和控制服务,通过unix socket将配置保存至ovsdb,并通过netlink和内核模块交互,主要负责基本的转发逻辑、地址学习、外部物理端口绑定、还可以运行ovs-ofctl工具,采用openflow协议对交换机进行远程配置和管理 -
ovsdb:ovs的数据库,保存了ovs的配置信息
Open vSwitch 内部分为用户态和内核态,用户态为守护程序实现了交换机和流表,是Open vSwitch的核心,提供了一些组件去管理交换机,实现数据库,对内核进行管理
数据流通过ovs的转发流程
1 数据包进入ovs
2 匹配流表
3 流表无匹配,向vswitchd中匹配转发规则
4 规则无匹配,向上传递个openflow 控制器判断是否转发,
5 无转发决策或者判断为否,将数据包drop
6 判断为可以转发的话,将数据包正常转发,并缓存流表
流表
每条流表规则由一些列字段组成,可以分为**基础字段** 、**匹配字段和动作字段**三部分。 在打印流表时,在流表中还存在一些显示字段,如duration,idle_age等,此处把这些字段也暂时归之于基础字段之中.
基础字段
cookie=value流表标识字段,cookie字段有两种书写方式:cookie=value和cookie=value/mask。mask中对应位为1时cookie中值相应的位须严格匹配,为0时cookie中值对应的位通配,当mask为-1时,必须严格匹配cookie值。duration=value流表生效时间,标识流表从下发到现在所持续的时间table=tableid流表所属表项,标识流表所属的表,默认为0priority=priority标识流表的优先级,范围为0-65535,值越大,优先级越高n_packets标识流表匹配包数n_bytes标识流表匹配字节数idle_timeout=sec流表空闲超时时间,流表会在空闲时间达到给定的时间时被删除。设置为0(默认值)时,流表不会因空闲时间被删除。hard_timeout=sec流表可存在的时间。设置此值后,流表会在到达给定时间后被删除。idle_age=sec流表空闲时间hard_age=sec流表存在时间。此字段与duration字段的区别在当流表被修改后,会重新设置hard_timer但是不会重置durationip_frag=frag_type当dl_type指定为IP或者IPv6,frag_type指定匹配的IP分片包或者非分片包的匹配frag_type支持的值为:no: 仅匹配非分片报文yes:匹配所有分片报文first:仅匹配offset为0的分片报文later: 仅匹配offset非0的分片报文not_later:匹配非分片报文和offset为0的分片报文
匹配字段
in_port=port标识匹配接收数据包的端口号dl_type=ethertype匹配数据包的二层协议类型,IP数据包为0x0800,IPv6数据包为0x86dd,ARP数据包为0x0806dl_src=xx:xx:xx:xx:xx:xxdl_dst=xx:xx:xx:xx:xx:xx匹配指定的链路层源或者目的MAC地址dl_src=xx:xx:xx:xx:xx:xx/xx:xx:xx:xx:xx:xxdl_dst=xx:xx:xx:xx:xx:xx/xx:xx:xx:xx:xx:xx匹配指定的链路层MAC地址,MAC地址格式为ADDR/MASK,当MASK值为01:00:00:00:00:00时,仅匹配多播位。当dl_dst=01:00:00:00:00:00/01:00:00:00:00:00时,匹配所有的组播报文和广播报文。dl_dst=00:00:00:00:00:00/01:00:00:00:00:00匹配所有的单播报文。nw_src=ip[/mask]nw_dst=ip[/mask]当dl_type=0x0800或指定ip时,匹配数据包的源、目的IP地址 当dl_type=0x0806或指定arp时,匹配ARP数据包的ar_spa或者ar_tpa字段dl_vlan=vlan匹配802.1Q类型(即vlan)数据包nw_proto=proto匹配数据包协议类型。当dl_type=0x0800时,匹配IP协议族的协议,例如tcp,udp,icmp等nw_tos=tos匹配IP Tos/DSCP或者IPv6的tos字段,值为0-255nw_ecn=ecn匹配IP或者IPv6的ecn字段,值为0~3nw_ttl=ttl匹配TTL值tp_src=porttp_dst=port若指定了udp或者tcp协议,则匹配udp/tcp的端口号icmp_type=typeicmp_code=code若指定了icmp或者icmpv6协议,则匹配对应的icmp 类型或者code字段arp_sha=xx:xx:xx:xx:xx:xxarp_tha=xx:xx:xx:xx:xx:xx当设置dl_type为ARP或者RARP,则arp_sha和arp_tha匹配数据包的源、目的MAC地址
动作字段
output:port将数据包从port接口发送enqueue:port:queue将数据包入队到指定端口的指定队列里normal将数据包按照设备上的正常L2/L3层处理方式进行处理flood将数据包发送到交换机上除接收接口和禁止flood的接口外的所有接口all将数据包发送到除接收接口外的所有接口controller(key=value…)将数据包作为PACKET IN消息发送到OpenFlow控制器。 支持的键值对:max_len=nbytes:限制发送到控制器的数据包长度字节数,默认情况是发送整个数据包;reason=reason:在PACKET IN消息中指明发送消息的原因,支持的reason为action(default),no_match和invalid_ttl;id=controller-id:指明控制器IDin_port将数据包从接收的接口发送出去drop丢弃数据包mod_vlan_vid:vlan_vid修改数据包的vlan idmod_vlan_pcp:vlan_pcp修改数据包的vlan prioritystrip_vlan如果数据包中存在vlan tag,则剥离vlan tagpush_vlan:ethertype为数据包添加新的vlan tagmod_dl_src:mac设置数据包的源MAC地址mod_dl_dst:mac设置数据包的目的MAC地址mod_nw_src:ip设置数据包的源IP地址mod_nw_dsp:ip设置数据包的目的IP地址mod_tp_src:port设置TCP或者UDP的源端口mod_tp_dst:port设置TCP或UDP的目的端口
ovs的相关命令
ovs-vsctl
ovs-vsctl:用于控制ovs db
ovs-ofctl:用于管理OpenFlow Switch的flow
ovs-dpctl:用于管理ovs的datapath
ovs-appctl:用于管理ovs daemon
ovs-vsctl
bridge相关
创建 bridge
ovs-vsctl add-br br0
删除 bridge
ovs-vsctl del-br br0
查看 bridge
ovs-vsctl list-br
# 固定网桥MAC地址
ovs-vsctl set bridge boc0 other-config:hwaddr=02:42:a9:28:9c:a7
# 设置网桥支持的OF协议
ovs-vsctl set bridge boc0 protocols=OpenFlow13
# 设置网桥ARP老化时间(最小15s)
ovs-vsctl set bridge br0 other_config:mac-aging-time=1
# 查看MAC缓存表
ovs-appctl fdb/show boc0
# 设置fail-mode
ovs-vsctl set-fail-mode bridge standalone|secure
ovs-vsctl get-fail-mode bridge
ovs-vsctl del-fail-mode bridge
ovs-vsctl set-fail-mode bridge standalone|secure
# 修改隧道协议
ovs-vsctl set Interface tun0 type=gre
port 相关
添加端口
添加端口eth1 到bridge中
ovs-vsctl add-port br0 eth1
创建 bond
在br0上创建一个bond了eth0,eth1和eth2的bond端口bond0
ovs-vsctl add-bond br0 bond0 eth0 eth1 eth2
移除端口
ovs-vsctl del-port br0 eth1
列出端口
ovs-vsctl list-ports br0
查看端口详细数据
列出ovs中端口eth1的详细数据
ovs-vsctl list interface eth1
# 添加数据口
ovs-vsctl add-port boc0 eth1 -- set port eth1 vlan_mode=trunk
# 添加隧道端口
ovs-vsctl add-port boc0 tun0 -- set interface tun0 type=vxlan options:remote_ip=flow options:key=flow
# 清除vlan tag
ovs-vsctl clear port eth1 tag
# 设置interface编号
ovs-vsctl add-port boc0 p1 -- set interface p1 type=internal ofport_request=10001
# 设置interface options 协议
ovs-vsctl set Interface tun0 type=vxlan options:remote_ip=flow options:csum="true" options:key=flow
控制器相关
添加控制器
ovs-vsctl set-controller br0 tcp:1.2.3.4:6633
# 添加多个控制器
ovs-vsctl set-controller br0 tcp:1.2.3.4:6633 tcp:4.3.2.1:6633
# 添加使用unix socket通信的controller
ovs-vsctl set-controller br0 unix:/var/run/xx/xx.sock
移除控制器
ovs-vsctl del-controller br0
查询bridge上配置的控制器
ovs-vsctl get-controller br0
vlan相关
配置端口为Access
# 设置br0中的端口eth0为VLAN 10的access口
ovs-vsctl set port eth0 tag=10
# 添加eth1到指定bridge br0中,同时将其配置成指定VLAN 10的access端口
ovs-svctl add-port br0 eth1 tag=10
配置端口为Trunk口
# 在br0上添加port eth1为VLAN 9,10,11的trunk
ovs-vsctl add-port br0 eth1 trunk=9,10,11
VXlan相关
在bridge ovs0中添加远端IP为10.10.10.1的VXLAN endpoint端口vxlan0
# key=100表示设置vni为100,不设置默认为0
ovs-vsctl add-port ovs0 vxlan0 -- set interface vxlan0 type=vxlan options:remote_ip=10.10.10.1 options:key=100
# 不设key值,vni默认为0
ovs-vsctl add-port ovs0 vxlan0 -- set interface vxlan0 type=vxlan options:remote_ip=10.10.10.1
# key=flow的话,表示该port的vni可以通过openflow的actions来进行设置
# 如: actions=set_field:100->tun_id
# 或: actions=set_tunnel:100
ovs-vsctl add-port ovs0 vxlan0 -- set interface vxlan0 type=vxlan options:remote_ip=10.10.10.1 options:key=flow
其它
Atomic operation
一条命令创建bridge br0的,并添加eth0到br0中
ovs-vsctl add-br br0 -- add-port br0 eth0
创建 internal port
OVS internal port 可以配置IP地址,普通 port 上配置的IP地址是不起作用的。在 br0 上创建一个internal port in0:
ovs-vsctl add-br br0 in0 -- set interface in0 type=internal
ip addr add 10.10.10.10/24 dev in0
# 创建internal port的同时将其设置为VLAN 10的access port
ovs-vsctl add-br br0 in1 tag=10 -- set interface in1 type=internal
ip addr add 20.20.20.20/24 dev in1
设置 OpenFlow port id
# 将已在ovs中的端口veth1的OpenFlow端口设置成100
ovs-vsctl set interface veth1 ofport_request=100
# 将端口veth1添加到bridge br0中,并将veth1的OpenFlow端口设置成200
ovs-vsctl add-port br0 veth1 -- set interface veth1 ofport_request=200
{% admonition note Note %} OpenFlow的端口 id 在设置 flow 的匹配字段 in_port 以及 actions 字段的 output 中都会用到。 可以通过命令ovs-ofctl show br0来查看 br0 中各端口的 OpenFlow 端口 id,该 id 并不求是按顺序的。 {% endadmonition %}
设置OpenFlow版本
ovs-vsctl set bridge br0 protocols=OpenFlow10,OpenFlow12,OpenFlow13
给ovs端口配置IP
ovs−vsctl add−port br-ex port tag=10 −− set Interface port type=internal # default is access
ifconfig port 192.168.100.1
配置流镜像
ovs-vsctl -- set Bridge br-int mirrors=@m -- --id=@tap6a094914-cd get Port tap6a094914-cd -- --id=@tap73e945b4-79 get Port tap73e945b4-79 -- --id=@tapa6cd1168-a2 get Port tapa6cd1168-a2 -- --id=@m create Mirror name=mymirror select-dst-port=@tap6a094914-cd,@tap73e945b4-79 select-src-port=@tap6a094914-cd,@tap73e945b4-79 output-port=@tapa6cd1168-a2
# clear
ovs-vsctl remove Bridge br0 mirrors mymirror
ovs-vsctl clear Bridge br-int mirrors
利用mirror特性对ovs端口patch-tun抓包
ip link add name snooper0 type dummy
ip link set dev snooper0 up
ovs-vsctl add-port br-int snooper0
ovs-vsctl -- set Bridge br-int mirrors=@m \
-- --id=@snooper0 get Port snooper0 \
-- --id=@patch-tun get Port patch-tun \
-- --id=@m create Mirror name=mymirror select-dst-port=@patch-tun \
select-src-port=@patch-tun output-port=@snooper0
# capture
tcpdump -i snooper0
# clear
ovs-vsctl clear Bridge br-int mirrors
ip link delete dev snooper0
如何配置QoS
# egress
$ ovs-vsctl -- \
add-br br0 -- \
add-port br0 eth0 -- \
add-port br0 vif1.0 -- set interface vif1.0 ofport_request=5 -- \
add-port br0 vif2.0 -- set interface vif2.0 ofport_request=6 -- \
set port eth0 qos=@newqos -- \
--id=@newqos create qos type=linux-htb \
other-config:max-rate=1000000000 \
queues:123=@vif10queue \
queues:234=@vif20queue -- \
--id=@vif10queue create queue other-config:max-rate=10000000 -- \
--id=@vif20queue create queue other-config:max-rate=20000000
$ ovs-ofctl add-flow br0 in_port=5,actions=set_queue:123,normal
$ ovs-ofctl add-flow br0 in_port=6,actions=set_queue:234,normal
# ingress
ovs-vsctl set interface vif1.0 ingress_policing_rate=10000
ovs-vsctl set interface vif1.0 ingress_policing_burst=8000
# clear
ovs-vsctl clear Port vif1.0 qos
ovs-vsctl list qos
ovs-vsctl destroy qos _uuid
ovs-vsctl list qos
ovs-vsctl destroy queue _uuid
ovs-ofctl
# 查看流表
ovs-ofctl dump-flows <table name>
# 清空流表
ovs-ofctl del-flows <table name>
# 添加表项让所有包本地转发
ovs-ofctl add-flow <table name> "table=0 priority=800 actions=NORMAL"
# in_port表示入端口,匹配到之后的actions是output:2,意思是从2端口转发出去
ovs-ofctl add-flow br0 in_port=1,actions=output:2