目录
OVS安装
OVS安装
CentOS
OVS常用命令参考
流表管理
流规则组成
Packet out (注包)
参考
OVS编译
直接源码编译安装
更新内核模块
编译RPM包
启用DPDK
编译内核模块
编译deb包
参考
OVS原理
OVS架构
主要模块职责
主要数据结构
主要流程
添加网桥
流表匹配
收包处理
upcall 消息处理
参考
OVS安装
OVS安装
CentOS
yum install centos-release-openstack-newton
yum install openvswitch
systemctl enable openvswitch
systemctl start openvswitch
如果想要安装master
版本,可以使用https://copr.fedorainfracloud.org/coprs/leifmadsen/ovs-master/的BUILD
:
wget -o /etc/yum.repos.d/ovs-master.repo https://copr.fedorainfracloud.org/coprs/leifmadsen/ovs-master/repo/epel-7/leifmadsen-ovs-master-epel-7.repo
yum install openvswitch openvswitch-ovn-*
OVS常用命令参考
如何添加bridge和port
ovs-vsctl add-br br0
ovs-vsctl del-br br0
ovs-vsctl list-br
ovs-vsctl add-port br0 eth0
ovs-vsctl set port eth0 tag=1 #vlan id
ovs-vsctl del-port br0 eth0
ovs-vsctl list-ports br0
ovs-vsctl show
给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
如何配置流监控sflow
ovs-vsctl -- --id=@s create sFlow agent=vif1.0 target=\"10.0.0.1:6343\" header=128 sampling=64 polling=10 -- set Bridge br-int sflow=@s
ovs-vsctl -- clear Bridge br-int sflow
如何配置流规则
ovs-ofctl add-flow br-int idle_timeout=0,in_port=2,dl_type=0x0800,dl_src=00:88:77:66:55:44,dl_dst=11:22:33:44:55:66,nw_src=1.2.3.4,nw_dst=5.6.7.8,nw_proto=1,tp_src=1,tp_dst=2,actions=drop
ovs-ofctl del-flows br-int in_port=2 //in_port=2的所有流规则被删除
ovs-ofctl dump-ports br-int
ovs-ofctl dump-flows br-int
ovs-ofctl show br-int //查看端口号
- 支持字段还有
nw_tos
,nw_ecn
,nw_ttl
,dl_vlan
,dl_vlan_pcp
,ip_frag
,arp_sha
,arp_tha
,ipv6_src
,ipv6_dst
等; - 支持流动作还有output:
port
,mod_dl_src/mod_dl_dst
,set field
等;
如何查看OVS的配置
ovs-vsctl list/set/get/add/remove/clear/destroy table record column [VALUE]
其中,TABLE名支持bridge
,controller
,interface
,mirror
,netflow
,open_vswitch
,port
,qos
,queue
,ssl,sflow
配置VXLAN/GRE
ovs-vsctl add-port br-ex port -- set interface port type=vxlan options:remote_ip=192.168.100.3
ovs−vsctl add−port br-ex port −− set Interface port type=gre options:remote_ip=192.168.100.3
ovs-vsctl set interface vxlan type=vxlan option:remote_ip=140.113.215.200 option:key=flow ofport_request=9
显示并学习MAC
ovs-appctl fdb/show br-ex
设置控制器地址
ovs-vsctl set-controller br-ex tcp:192.168.100.1:6633
ovs-vsctl get-controller br0
流表管理
流规则组成
每条流规则由一系列字段组成,分为基本字段、条件字段和动作字段三部分:
- 基本字段包括生效时间
duration_sec
、所属表项table_id
、优先级priority
、处理的数据包数n_packets
,空闲超时时间idle_timeout
等,空闲超时时间idle_timeout
以秒为单位,超过设置的空闲超时时间后该流规则将被自动删除,空闲超时时间设置为0表示该流规则永不过期,idle_timeout
将不包含于ovs-ofctl dump-flows brname
的输出中。 - 条件字段包括输入端口号
in_port
、源目的mac
地址dl_src/dl_dst
、源目的ip
地址nw_src/nw_dst
、数据包类型dl_type
、网络层协议类型nw_proto
等,可以为这些字段的任意组合,但在网络分层结构中底层的字段未给出确定值时上层的字段不允许给确定值,即一条流规则中允许底层协议字段指定为确定值,高层协议字段指定为通配符(不指定即为匹配任何值),而不允许高层协议字段指定为确定值,而底层协议字段却为通配符(不指定即为匹配任何值),否则,ovs-vswitchd
中的流规则将全部丢失,网络无法连接。 - 动作字段包括正常转发
normal
、定向到某交换机端口output
:port
、丢弃drop
、更改源目的mac
地址mod_dl_src/mod_dl_dst
等,一条流规则可有多个动作,动作执行按指定的先后顺序依次完成。
流规则中可包含通配符和简写形式,任何字段都可等于*
或ANY
,如丢弃所有收到的数据包
ovs-ofctl add-flow xenbr0 dl_type=*,nw_src=ANY,actions=drop
简写形式为将字段组简写为协议名,目前支持的简写有ip
,arp
,icmp
,tcp
,udp
,与流规则条件字段的对应关系如下:
dl_type=0x0800 <=>ip
dl_type=0x0806 <=>arp
dl_type=0x0800,nw_proto=1 <=> icmp
dl_type=0x0800,nw_proto=6 <=> tcp
dl_type=0x0800,nw_proto=17 <=> udp
dl_type=0x86dd. <=> ipv6
dl_type=0x86dd,nw_proto=6. <=> tcp6
dl_type=0x86dd,nw_proto=17. <=> udp6
dl_type=0x86dd,nw_proto=58. <=> icmp6
屏蔽某个IP
ovs-ofctl add-flow xenbr0 idle_timeout=0,dl_type=0x0800,nw_src=119.75.213.50,actions=drop
数据包重定向
ovs-ofctl add-flow xenbr0 idle_timeout=0,dl_type=0x0800,nw_proto=1,actions=output:4
去除VLAN tag
ovs-ofctl add-flow xenbr0 idle_timeout=0,in_port=3,actions=strip_vlan,normal
更改数据包源IP地址后转发
ovs-ofctl add-flow xenbr0 idle_timeout=0,in_port=3,actions=mod_nw_src:211.68.52.32,normal
注包
# 格式为:ovs-ofctl packet-out switch in_port actions packet
# 其中,packet为hex格式数据包
ovs-ofctl packet-out br2 none output:2 040815162342FFFFFFFFFFFF07C30000
流表常用字段
-
in_port=port
传递数据包的端口的OpenFlow
端口编号 -
dl_vlan=vlan
数据包的VLAN Tag
值,范围是0-4095
,0xffff
代表不包含VLAN Tag
的数据包 -
dl_src=<MAC>和dl_dst=<MAC>
匹配源或者目标的MAC
地址01:00:00:00:00:00/01:00:00:00:00:00
代表广播地址00:00:00:00:00:00/01:00:00:00:00:00
代表单播地址 -
dl_type=ethertype
匹配以太网协议类型,其中:dl_type=0x0800
代表IPv4
协议dl_type=0x086dd
代表IPv6
协议dl_type=0x0806
代表ARP
协议 -
nw_src=ip[/netmask]
和nw_dst=ip[/netmask]
当dl_typ=0x0800
时,匹配源或者目标的IPv4
地址,可以使IP
地址或者域名 -
nw_proto=proto
和dl_type
字段协同使用。当dl_type=0x0800
时,匹配IP
协议编号;当dl_type=0x086dd
代表IPv6
协议编号 -
table=number
指定要使用的流表的编号,范围是0-254
。在不指定的情况下,默认值为0
。通过使用流表编号,可以创建或者修改多个Table
中的Flow
-
reg<idx>
=value[/mask]
交换机中的寄存器的值。当一个数据包进入交换机时,所有的寄存器都被清零,用户可以通过Action
的指令修改寄存器中的值
常见的操作
-
output:port
: 输出数据包到指定的端口。port
是指端口的OpenFlow
端口编号 -
mod_vlan_vid
: 修改数据包中的VLAN tag
-
strip_vlan
: 移除数据包中的VLAN tag
-
mod_dl_src/ mod_dl_dest
: 修改源或者目标的MAC
地址信息 -
mod_nw_src/mod_nw_dst
: 修改源或者目标的IPv4
地址信息 -
resubmit:port
: 替换流表的in_port
字段,并重新进行匹配 -
load:value−>dst[start..end]
: 写数据到指定的字段
跟踪数据包的处理过程
ovs-appctl ofproto/trace br0 in_port=3,tcp,nw_src=10.0.0.2,tcp_dst=22
ovs-appctl ofproto/trace br-int \
in_port=1,dl_src=00:00:00:00:00:01,\
dl_dst=00:00:00:00:00:02 -generate
Packet out (注包)
import binascii
from scapy.all import *
a=Ether(dst="02:ac:10:ff:00:22",src="02:ac:10:ff:00:11")/IP(dst="172.16.255.22",src="172.16.255.11", ttl=10)/ICMP()
print binascii.hexlify(str(a))
ovs-ofctl packet-out br-int 5 "normal" 02AC10FF002202AC10FF001108004500001C000100000A015A9DAC10FF0BAC10FF160800F7FF00000000
参考
- http://openvswitch.org/
- http://blog.scottlowe.org/
- https://blog.russellbryant.net/
- Comparing OpenStack Neutron ML2+OVS and OVN – Control Plane
OVS编译
直接源码编译安装
export OVS_VERSION="2.6.1"
export OVS_DIR="/usr/src/ovs"
export OVS_INSTALL_DIR="/usr"
curl -sSl http://openvswitch.org/releases/openvswitch-${OVS_VERSION}.tar.gz | tar -xz && mv openvswitch-${OVS_VERSION} ${OVS_DIR}
cd ${OVS_DIR}
./boot.sh
# 如果启用DPDK,还需要加上--with-dpdk=/usr/local/share/dpdk/x86_64-native-linuxapp-gcc
./configure --prefix=${OVS_INSTALL_DIR} --localstatedir=/var --enable-ssl --with-linux=/lib/modules/$(uname -r)/build
make -j `nproc`
make install
make modules_install
更新内核模块
cat > /etc/depmod.d/openvswitch.conf << EOF
override openvswitch * extra
override vport-* * extra
EOF
depmod -a
cp debian/openvswitch-switch.init /etc/init.d/openvswitch-switch
/etc/init.d/openvswitch-switch force-reload-kmod
编译RPM包
make rpm-fedora RPMBUILD_OPT="--without check"
启用DPDK
make rpm-fedora RPMBUILD_OPT="--with dpdk --without check"
编译内核模块
make rpm-fedora-kmod
编译deb包
apt-get install build-essential fakeroot
dpkg-checkbuilddeps
# 已经编译过,需要首先clean
# fakeroot debian/rules clean
DEB_BUILD_OPTIONS='parallel=8 nocheck' fakeroot debian/rules binary
参考
- Installing Open vSwitch
- Debian Packaging for Open vSwitch
- Open vSwitch with DPDK
- Open vSwitch on Linux, FreeBSD and NetBSD - Hot Upgrading
OVS原理
OVS架构
图片来源 2015 FOSDEM - OVS Stateful Services
ovs
的架构如上图所示,主要由内核datapath
和用户空间的vswitchd
、ovsdb
组成。
主要模块职责
-
datapath
是负责数据交换的内核模块,其从网口读取数据,并快速匹配Flowtable中的流表项,成功的直接转发,失败的上交vswitchd
处理。它在初始化和port binding
的时候注册钩子函数,把端口的报文处理接管到内核模块。 -
vswitchd
是一个守护进程,是ovs的管理和控制服务,通过unix socket将配置信息保存到ovsdb
,并通过netlink
和内核模块交互 -
ovsdb
则是ovs
的数据库,保存了ovs
配置信息
主要数据结构
(图片来自 csdn)
主要流程
注:部分转载自OVS 源码分析整理
添加网桥
- 键入命令
ovs-vsctl add-br testBR
- 内核中的
openvswitch.ko
收到一个添加网桥的命令时候——即收到OVS_DATAPATH_FAMILY
通道的OVS_DP_CMD_NEW
命令。该命令绑定的回调函数为ovs_dp_cmd_new
-
ovs_dp_cmd_new
函数除了初始化dp
结构外,调用new_vport
函数来生成新的vport
-
new_vport
函数调用ovs_vport_add()
来尝试生成一个新的vport
-
ovs_vport_add()
函数会检查vport
类型(通过vport_ops_list[]
数组),并调用相关的 create()函数来生成vport
结构 - 当dp是网络设备时(
vport_netdev.c
),最终由ovs_vport_add()
函数调用的是netdev_create()
【在vport_ops_list
的ovs_netdev_ops
中】 -
netdev_create()
函数最关键的一步是注册了收到网包时的回调函数 err=netdev_rx_handler_register(netdev_vport->dev,netdev_frame_hook,vport);
- 操作是将
netdev_vport->dev
收到网包时的相关数据由netdev_frame_hook()
函数来处理,都是些辅助处理,依次调用各处理函数,在netdev_port_receive()
【这里会进行数据包的拷贝,避免损坏】进入 ovs_vport_receive()回到vport.c
,从ovs_dp_process_receive_packet()
回到datapath.c
,进行统一处理 - 流程:
netdev_frame_hook()->netdev_port_receive->ovs_vport_receive->ovs_dp_process_received_packet()
-
net_port_receive()
首先检测是否 skb 被共享,若是则得到packet
的拷贝。 -
net_port_receive()
其调用ovs_vport_receive()
,检查包的校验和,然后交付给我们的vport
通用层来处理。
(图片来自 简书)
流表匹配
-
flow_lookup()
查找对应的流表项 -
for
循环调用rcu_dereference_ovs
对流表结构体中的mask_list
成员遍历,找到对应的的 成员 -
flow=masked_flow_lookup()
遍历进行下一级hmap
查找,找到为止 - 进入 包含函数
ovs_flow_mask_key(&masked_key,unmasked,mask)
,将最开始提取的Key
值和mask
的key
值进行“与”操作,结果存放在masked_key
中,用来得到后面的Hash
值 -
hash=flow_hash(&masked_key,key_start,key_end)key
值的匹配字段只有部分 -
ovs_vport_add()
函数会检查vport
类型(通过vport_ops_list[]
数组),并调用相关的create()
函数来生成vport
结构 - 可见,当
dp
时网络设备时(vport_netdev.c)
,最终由ovs_vport_add()
函数调用的是netdev_create()
【在vport_ops_list
的ovs_netdev_ops
中】 -
netdev_vport->dev
收到网包时的相关数据由netdev_frame_hook()
函数来处理,都是些辅助处理,依次调用各处理函数,在netdev_port_receive()
【这里会进行数据包的拷贝,避免损坏】进入ovs_vport_receive()
回到vport.c
,从ovs_dp_process_receive_packet()
回到datapath.c
,进行统一处理
收包处理
-
ovs_vport_receive_packets()
调用ovs_flow_extract
基于skb
生成key
值,并检查是否有错,然后调用ovs_dp_process_packet
。交付给datapath
处理 -
ovs_flow_tbl_lookup_stats
。基于前面生成的key
值进行流表查找,返回匹配的流表项,结构为sw_flow
。 - 若不存在匹配,则调用
ovs_dp_upcall
上传至userspace
进行匹配。 (包括包和key
都要上传) - 若存在匹配,则直接调用
ovs_execute_actions
执行对应的action
,比如添加vlan
头,转发到某个port等。
upcall 消息处理
-
ovs_dp_upcall()
首先调用err=queue_userspace_packet()
将信息排队发到用户空间去 -
dp_ifindex=get_dpifindex(dp)
获取网卡设备索引号 - 调整
VLAN
的MAC
地址头指针 - 网络链路属性,如果不需要填充则调用此函数
-
len=upcall_msg_size()
,获得upcall
发送消息的大小 -
user_skb=genlmsg_new_unicast
,创建一个新的netlink
消息 -
upcall=genlmsg_put()
增加一个新的netlink
消息到skb
-
err=genlmsg_unicast()
,发送消息到用户空间去处理
参考
- OpenvSwitch Deep Dive
- 2015 FOSDEM - OVS Stateful Services
- OVS 源码分析整理
- openvswitch源码分析
- OVS Deep Dive