Open vSwitch is a production quality, multilayer virtual switch licensed under the open source Apache 2.0 license. It is designed to enable massive network automation through programmatic extension, while still supporting standard management interfaces and protocols (e.g. NetFlow, sFlow, IPFIX, RSPAN, CLI, LACP, 802.1ag). In addition, it is designed to support distribution across multiple physical servers similar to VMware’s vNetwork distributed vswitch or Cisco’s Nexus 1000V.
– 官方网站
Open vSwitch(下文简称 OvS)就是一个开源的虚拟交换机实现。广泛应用在云计算行业,为网络管理员提供虚拟云主机之间和之内的流量可见性与可控性。Open vSwitch 旨在用虚拟化方案解决网络问题,与控制器软件一起实现分布式的虚拟交换技术。这意味着,交换机和控制器软件能够在多个服务器之间创建集群网络配置,从而不需要在每一台云主机和物理主机上单独配置网络。这个交换机还支持 VLAN 中继,通过 NetFlow、sFlow 和 RSPAN 实现可见性,通过 OpenFlow 协议进行管理。它还有其他一些特性:严格流量控制,它由 OpenFlow 交换协议实现;远程管理功能,它能通过网络策略实现更多控制。
OpenFlow is a open standard managed by Open Networking Foundation. It specifies a protocol by which a remote controller can modify the behavior of networking devices through a well-defined “forwarding instruction set“.
在虚拟交换机的 Flow 控制器或管理工具方面,OvS 需要借助第三方控制器或管理工具实现复杂的转发策略。例如 OvS 支持 OpenFlow 协议,我们就可以使用任何支持 OpenFlow 协议的控制器来对 OvS 进行远程管理。但这并不意味着 OvS 必须要有一个控制器才能工作。在不连接外部控制器情况下,OvS 自身可以依靠 MAC 地址学习实现二层数据包转发功能,就像 Linux Bridge。
简而言之,Open vSwitch 即开放的 OpenFlow 交换机,能够达到产品级的质量,也就是说可以部署一些生产环境使用。它不光支持基本的二层交换,还支持标准的管理机接口和协议(e.g. NetFlow,sFlow,SPAN,RSAPN,CLI,LACP,802.1ag),可以很好的与 SDN 体系融合。
Open vSwitch 的特性清单:
OvS 在用户空间主要组件有 ovsdb-server 和 ovs-vswitchd。内和空间中由 datapath 内核模块。最上面的 Controller 表示 OpenFlow 控制器,控制器与 OvS 是通过 OpenFlow 协议进行连接。
是 OvS 轻量级的数据库服务,用于存储整个 OvS 的配置信息,包括接口,交换内容,VLAN,虚拟交换机的创建,网卡的添加等信息与操作记录。都被 ovsdb 保存到一个 conf.db 文件(JSON 格式)里面,通过 db.sock 提供服务。OvS 主进程 ovs-vswitchd 根据数据库中的配置信息工作。
ovsdb-server /etc/openvswitch/conf.db
-vconsole:emer -vsyslog:err
-vfile:info
--remote=punix:/var/run/openvswitch/db.sock
--private-key=db:Open_vSwitch,SSL,private_key
--certificate=db:Open_vSwitch,SSL,certificate
--bootstrap-ca-cert=db:Open_vSwitch,SSL,ca_cert --no-chdir
--log-file=/var/log/openvswitch/ovsdb-server.log
--pidfile=/var/run/openvswitch/ovsdb-server.pid
--detach
--monitor
/etc/openvswitch/conf.db
:是数据库文件存放位置,ovsdb-server 需要该文件才能启动,可以使用 ovsdb-tool create
命令创建并初始化此数据库文件。--remote=punix:/var/run/openvswitch/db.sock
:实现了一个 Unix Sockets 连接,OvS 主进程 ovs-vswitchd 或其它命令工具(e.g. ovsdb-client) 通过这个 Socket 连接来管理 ovsdb。/var/log/openvswitch/ovsdb-server.log
:ovsdb-server 的运行日志文件。ovs-vswitchd 本质是一个守护进程,是 OvS 的核心部件。ovs-vswitchd 和 Datapath 一起实现 OvS 基于流表(Flow-based Switching)的数据交换。它通过 OpenFlow 协议可以与 OpenFlow 控制器通信,使用 ovsdb 协议与 ovsdb-server 数据库服务通信,使用 netlink 和 Datapath 内核模块通信。ovs-vswitchd 支持多个独立的 Datapath,ovs-vswitchd 需要加载 Datapath 内核模块才能正常运行。ovs-vswitchd 在启动时读取 ovsdb-server 中的配置信息,然后自动配置 Datapaths 和 OvS Switches 的 Flow Tables,所以用户不需要额外的通过执行 ovs-dpctl
指令工具去操作 Datapath。当 ovsdb 中的配置内容被修改,ovs-vswitched 也会自动更新其配置以保持数据同步。ovs-vswitchd 也可以从 OpenFlow 控制器获取流表项。
ovs-vswitchd unix:/var/run/openvswitch/db.sock
-vconsole:emer
-vsyslog:err
-vfile:info
--mlockall
--no-chdir
--log-file=/var/log/openvswitch/ovs-vswitchd.log
--pidfile=/var/run/openvswitch/ovs-vswitchd.pid
--detach
--monitor
在 OpenFlow Switch 规则的语义中,给交换机,或者桥,用了一个专业的名词,叫做 Datapath!Open vSwitch 的内核模块 openvswitch.ko 实现了多个 Datapath,每个 Datapath 可以具有多个 Ports。每个 Datapath 通过关联流表(Flow Table)来定义网络包的流向。Datapath 监听网卡接口设备,将监听到的数据包首先在流表中进行匹配,找到匹配的流表项之后把对应的 Actions 返回给 Datapath,作为数据处理行为的描述。Datapath 支持数据在内核空间进行交换。Datapath 的内核模块信息:
# modinfo openvswitch
filename: /lib/modules/3.10.0-327.el7.x86_64/kernel/net/openvswitch/openvswitch.ko
license: GPL
description: Open vSwitch switching datapath
rhelversion: 7.2
srcversion: F75F2B83324DCC665887FD5
depends: libcrc32c
intree: Y
...
更多 Datapath 的信息可以浏览《The Design and Implementation of Open vSwitch》。
The datapath module in the kernel receives the packets first, from a physical NIC or a VM’s virtual NIC. Either ovs-vswitchd has instructed the datapath how to handle packets of this type, or it has not. In the former case, the datapath module simply follows the instructions, called actions, given by ovs-vswitchd, which list physical ports or tunnels on which to transmit the packet. Actions may also specify packet modifications, packet sampling, or instructions to drop the packet. In the other case, where the datapath has not been told what to do with the packet, it delivers it to ovs-vswitchd. In userspace, ovs-vswitchd determines how the packet should be handled, then it passes the packet back to the datapath with the desired handling. Usually, ovs-vswitchd also tells the datapath to cache the actions, for handling similar future packets.
Bridge 处理数据帧遵循以下几条规则:
用户空间 ovs-vswitchd 和内核模块 Datapath 决定了数据包的转发,如下图:
上述,Datapath 和 ovs-vswitchd 相互配合中包含了两种网络包的处理方式:
Fast Path:Datapatch 加载到内核后,会在网卡上注册一个钩子函数,每当有网络包到达网卡时,这个函数就会被调用,将网络包开始层层拆包(MAC 层,IP 层,TCP 层等),然后与流表项匹配,如果找到匹配的流表项则根据既定策略来处理网络包(e.g. 修改 MAC,修改 IP,修改 TCP 端口,从哪个网卡发出去等等),再将网络包从网卡发出。这个处理过程全在内核完成,所以非常快,称之为 Fast Path。
Slow Path:内核态并没有被分配太多内存,所以内核态能够保存的流表项很少,往往有新的流表项到来后,老的流表项就被丢弃。如果在内核态找不到流表项,则需要到用户态去查询,网络包会通过 netlink(一种内核态与用户态交互的机制)发送给 ovs-vswitchd,ovs-vswitchd 有一个监听线程,当发现有从内核态发过来的网络包,就进入自己的处理流程,然后再次将网络包重新注入到 Datapath。显然,在用户态处理是相对较慢的,故称值为 Slow Path。
在用户态的 ovs-vswtichd 不需要吝啬内存,它包含了所有流表项,这些流表项可能是 OpenFlow 控制器通过 OpenFlow 协议下发的,也可能是 OvS 命令行工具 ovs-ofctl 设定的。ovs-vswtichd 会根据网络包的信息层层匹配,直到找到一款流表项进行处理。如果实在找不到,则一般会采用默认流表项,比如丢弃这个包。
当最终匹配到了一个流表项之后,则会根据 “局部性原理(局部数据在一段时间都会被频繁访问,是缓存设计的基础原理)” 再通过 netlink 协议,将这条策略下发到内核态,当这条策略下发给内核时,如果内核的内存空间不足,则会开始淘汰部分老策略。这样保证下一个相同类型的网络包能够直接从内核匹配到,以此加快执行效率。由于近因效应,接下来的网络包应该大概率能够匹配这条策略的。例如:传输一个文件,同类型的网络包会源源不断的到来。
# 查看 OvS Log 级别
ovs-appctl vlog/list
# 设置 Log 级别
ovs-appctl vlog/set {module name}:{console, syslog, file}:{off, emer, err, warn, info, dbg}
ovs-appctl vlog/set stp:file:dbg
# 查看 OvS 版本
ovs-ofctl -V
# 查询指令历史记录
ovsdb-tool show-log [-mmm]
# 修改 OpenFlow 网络端口编号
ovs-vsctl add-port -- set Interface ofport_request=100
# 设置 Interface 类型为 internal
ovs-vsctl set Interface type=internal
# 开启指定 Bridge 的 STP
ovs-vsctl set bridge stp_enable=true
# 关闭指定 Bridge 的 STP
ovs-vsctl set bridge stp_enable=false
# 查询指定 Bridge 的 STP 的配置信息
ovs-vsctl get bridge stp_enable
# 设置指定 Bridge 的 STP Priority
ovs−vsctl set bridge other_config:stp-priority=0x7800
# 设置指定 Bridge 的 STP Cost
ovs−vsctl set port other_config:stp-path-cost=10
OS:CentOS7
Step1. 关闭 SELinux,否则 ovsdb-server Manager 无法正常工作。
[root@localhost ~]# setenforce 0
[root@localhost ~]# cat /etc/selinux/config | grep -v ^#
SELINUX=disabled
SELINUXTYPE=targeted
Step 2. yum install
yum install openvswitch openvswitch-test
Step 3. 启动服务
systemctl enable openvswitch
systemctl start openvswitch
systemctl status openvswitch
查看当前的 OvS 版本:
[root@localhost ~]# ovs-vsctl show
2028eafc-e1db-4ea8-b0fc-30a21fdaca0f
ovs_version: "2.0.0"
查看 OvS 服务进程清单:
ovsdb-server /etc/openvswitch/conf.db
-vconsole:emer -vsyslog:err
-vfile:info
--remote=punix:/var/run/openvswitch/db.sock
--private-key=db:Open_vSwitch,SSL,private_key
--certificate=db:Open_vSwitch,SSL,certificate
--bootstrap-ca-cert=db:Open_vSwitch,SSL,ca_cert --no-chdir
--log-file=/var/log/openvswitch/ovsdb-server.log
--pidfile=/var/run/openvswitch/ovsdb-server.pid
--detach
--monitor
ovs-vswitchd unix:/var/run/openvswitch/db.sock
-vconsole:emer
-vsyslog:err
-vfile:info
--mlockall
--no-chdir
--log-file=/var/log/openvswitch/ovs-vswitchd.log
--pidfile=/var/run/openvswitch/ovs-vswitchd.pid
--detach
--monitor
查看加载的内核模块:
[root@localhost ~]# lsmod | grep openvswitch
openvswitch 70743 0
vxlan 37584 1 openvswitch
gre 13808 1 openvswitch
libcrc32c 12644 2 xfs,openvswitch
ovs-db 在操作系统上的载体是 JSON 文件 /etc/openvswitch/conf.db,通过执行指令 ovsdb-client dump
可以查看其内容,e.g.
[root@localhost ~]# ovsdb-client dump
Bridge table
_uuid controller datapath_id datapath_type external_ids fail_mode flood_vlans flow_tables ipfix mirrors name netflow other_config ports protocols sflow status stp_enable
----- ---------- ----------- ------------- ------------ --------- ----------- ----------- ----- ------- ---- ------- ------------ ----- --------- ----- ------ ----------
Controller table
_uuid connection_mode controller_burst_limit controller_rate_limit enable_async_messages external_ids inactivity_probe is_connected local_gateway local_ip local_netmask max_backoff other_config role status target
----- --------------- ---------------------- --------------------- --------------------- ------------ ---------------- ------------ ------------- -------- ------------- ----------- ------------ ---- ------ ------
Flow_Sample_Collector_Set table
_uuid bridge external_ids id ipfix
----- ------ ------------ -- -----
Flow_Table table
_uuid flow_limit groups name overflow_policy
----- ---------- ------ ---- ---------------
IPFIX table
_uuid cache_active_timeout cache_max_flows external_ids obs_domain_id obs_point_id sampling targets
----- -------------------- --------------- ------------ ------------- ------------ -------- -------
Interface table
_uuid admin_state bfd bfd_status cfm_fault cfm_fault_status cfm_health cfm_mpid cfm_remote_mpids cfm_remote_opstate duplex external_ids ifindex ingress_policing_burst ingress_policing_rate lacp_current link_resets link_speed link_state mac mac_in_use mtu name ofport ofport_request options other_config statistics status type
----- ----------- --- ---------- --------- ---------------- ---------- -------- ---------------- ------------------ ------ ------------ ------- ---------------------- --------------------- ------------ ----------- ---------- ---------- --- ---------- --- ---- ------ -------------- ------- ------------ ---------- ------ ----
Manager table
_uuid connection_mode external_ids inactivity_probe is_connected max_backoff other_config status target
----- --------------- ------------ ---------------- ------------ ----------- ------------ ------ ------
Mirror table
_uuid external_ids name output_port output_vlan select_all select_dst_port select_src_port select_vlan statistics
----- ------------ ---- ----------- ----------- ---------- --------------- --------------- ----------- ----------
NetFlow table
_uuid active_timeout add_id_to_interface engine_id engine_type external_ids targets
----- -------------- ------------------- --------- ----------- ------------ -------
Open_vSwitch table
_uuid bridges cur_cfg db_version external_ids manager_options next_cfg other_config ovs_version ssl statistics system_type system_version
------------------------------------ ------- ------- ---------- -------------------------------------------------- --------------- -------- ------------ ----------- --- ---------- ----------- --------------
2028eafc-e1db-4ea8-b0fc-30a21fdaca0f [] 0 "7.3.0" {system-id="257b9b47-87e7-4404-9af8-37f98b04f2bd"} [] 0 {} "2.0.0" [] {} unknown unknown
Port table
_uuid bond_downdelay bond_fake_iface bond_mode bond_updelay external_ids fake_bridge interfaces lacp mac name other_config qos statistics status tag trunks vlan_mode
----- -------------- --------------- --------- ------------ ------------ ----------- ---------- ---- --- ---- ------------ --- ---------- ------ --- ------ ---------
QoS table
_uuid external_ids other_config queues type
----- ------------ ------------ ------ ----
Queue table
_uuid dscp external_ids other_config
----- ---- ------------ ------------
SSL table
_uuid bootstrap_ca_cert ca_cert certificate external_ids private_key
----- ----------------- ------- ----------- ------------ -----------
sFlow table
_uuid agent external_ids header polling sampling targets
----- ----- ------------ ------ ------- -------- -------
使用图形化工具可以更加友好的查看:
数据库表之间的关系如下图所示:
其中 Open_vSwitch 表是 OvS DB 的 root(根)。
它的表结构如下图,记录 ovs-vswitchd 的配置信息,e.g.
数据库结构是一款软件的资源模型设计的映射,下文主要根据 OvS 的资源模型来依次认识每种资源对象的特性与作用。
Manager 对象都是为了配置 ovsdb-server 的 Connection,让 Clients(e.g. ovs-vswitchd、ovs-vsctl、host) 可以远程对 ovsdb-server 执行 DB Operation。
从上述架构图可知,ovsdb-server 是 ovs-db 提供管理的 RPC 接口。加载了 Open_vSwitch 表中的 manager_options 字段值来作为监听端口。
Manager 的表结构:
其中最重要的字段是 target,记录了 ovsdb-server 的监听参数信息:
Active(主动)database connection methods:
ssl:ip[:port]
:监听在指定 Remote IP 的 Port 上,协议为 SSLtcp:ip[:port]
:监听在指定 Remote IP 的 Port 上,协议为 TCPunix:FILE
:Unix domain socket named FILEPassive(被动)database connection methods:
pssl:[port][:ip]
:监听在本机 IP 的指定 Port 上,协议为 SSLptcp:[port][:ip]
:监听在本机 IP 的指定 Port 上,协议为 TCP通过下述指令设置:
ovs-vsctl set-manager TARGET... # set the list of managers to TARGET...
# Active Listener
ovs-vsctl set-manager tcp:1.2.3.4:6640
# Passive Listener
ovs-vsctl set-manager ptcp:6640
NOTE:基于 TCP 的 DB Connection,使得 ovs-vsctl 在远程机器上也可以控制 ovsdb-server。
[root@localhost ~]# ovs-vsctl show
2028eafc-e1db-4ea8-b0fc-30a21fdaca0f
ovs_version: "2.0.0"
[root@localhost ~]# ovs-vsctl set-manager ptcp:8881
[root@localhost ~]# ovs-vsctl show
2028eafc-e1db-4ea8-b0fc-30a21fdaca0f
Manager "ptcp:8881"
ovs_version: "2.0.0"
[root@localhost ~]# ovsdb-client dump
...
Manager table
_uuid connection_mode external_ids inactivity_probe is_connected max_backoff other_config status target
------------------------------------ --------------- ------------ ---------------- ------------ ----------- ------------ -------------------------------------------------------------------- -----------
84f0a33c-a798-40fc-a285-ed4d83121d3e [] {} [] false [] {} {bound_port="8881", sec_since_connect="0", sec_since_disconnect="0"} "ptcp:8881"
检查 Port 是否正常开启:
[root@localhost ~]# netstat -lpntu | grep 8881
tcp 0 0 0.0.0.0:8881 0.0.0.0:* LISTEN 3024/ovsdb-server
从远程计算机上执行连接:
[root@localhost ~]# ovs-vsctl --db=tcp:192.168.1.109:8881 show
2028eafc-e1db-4ea8-b0fc-30a21fdaca0f
Manager "ptcp:8881"
ovs_version: "2.0.0"
NOTE:注意防火墙的干扰因素。
如果将 Open vSwitch 配置为通过网络连接到 OpenFlow 控制器(ovs-vswitchd 与 ovs-controller 之间),那么建议你使用 OpenSSL 来构建安全的网络通讯,双向的 SSL 互相可确保 OpenFlow 连接的完整性和与安全。
想建立 SSL 连接,首先要获取相关的 CA 证书,并将这些证书记录到 SSL 表中。启动 ovsdb-server 时,通过选项 --private-key=db:Open_vSwitch,SSL,private_key --certificate=db:Open_vSwitch,SSL,certificate --bootstrap-ca-cert=db:Open_vSwitch,SSL,ca_cert
来指定应用这些参数。
其中主机(客户端)的 Public key 包含在 CA 签发的证书(certificate)内,然后再由 CA 中心的 private key 进行签名,CA 中心来担保这个 certificate 是合法的。为了验证 CA 签名,还需要 CA 的 public key,并放到 ca_cert 指向的 CA 证书里面。而 CA 本身的 public key 也需要被签名更高级的 CA 或者 CA(根 CA)自己担保。
SSL 验证流程:
# 查询 SSL 连接
ovs-vsctl get-ssl
# 设置 SSL 证书
ovs-vsctl set-ssl sc-privkey.pem sc-cert.pem cacert.pem
# 删除 SSL 连接
ovs-vsctl del-ssl
自签发 CA 根证书:
mkdir ~/OVS_CA
cd ~/OVS_CA
openssl genrsa -out caprivate.key 1024
openssl req -key caprivate.key -new -subj "/C=CN/ST=CN/L=CN/O=CN/[email protected]" -out cacertificate.req
openssl x509 -req -in cacertificate.req -signkey caprivate.key -out cacertificate.pem
PS:自签发即自己担保自己,用自己的私钥对自己的 CSR 进行签发,所以也称为根证书。
签发客户端证书:
mkdir ~/ClientCerts
cd ~/ClientCerts
openssl genrsa -out cliu8private.key 1024
openssl req -key cliu8private.key -new -subj "/C=CN/ST=CN/L=CN/O=CN/[email protected]" -out cliu8certificate.req
cp ~/OVS_CA/caprivate.key ~/OVS_CA/cacertificate.pem ~/ClientCerts
openssl x509 -req -in cliu8certificate.req -CA cacertificate.pem -CAkey caprivate.key -out cliu8certificate.pem -CAcreateserial
配置 SSL Connection:
ovs-vsctl del-manager
ovs-vsctl set-manager pssl:8881
ovs-vsctl set-ssl /root/ClientCerts/cliu8private.key /root/ClientCerts/cliu8certificate.pem /root/OVS_CA/cacertificate.pem
NOTE:set-ssl 一定要指定绝对路径,否则无法正确载入证书文件。
查看修改:
[root@localhost newcerts]# ovs-vsctl show
2028eafc-e1db-4ea8-b0fc-30a21fdaca0f
Manager "pssl:8881"
ovs_version: "2.0.0"
[root@localhost ~]# ovsdb-client dump
...
SSL table
_uuid bootstrap_ca_cert ca_cert certificate external_ids private_key
------------------------------------ ----------------- -------------------------------- ---------------------------------------- ------------ ------------------------------------
f8d8f0f5-f87b-430b-9d48-123a74a6804f false "/root/OVS_CA/cacertificate.pem" "/root/ClientCerts/cliu8certificate.pem" {} "/root/ClientCerts/cliu8private.key"
验证:
[root@localhost ~]# ovs-vsctl --db=ssl:192.168.1.109:8881 show
2019-02-23T06:46:26Z|00001|stream_ssl|ERR|Private key must be configured to use SSL
2019-02-23T06:46:26Z|00002|stream_ssl|ERR|Certificate must be configured to use SSL
2019-02-23T06:46:26Z|00003|stream_ssl|ERR|CA certificate must be configured to use SSL
2019-02-23T06:46:26Z|00004|reconnect|WARN|ssl:192.168.1.109:8881: connection attempt failed (Protocol not available)
ovs-vsctl: ssl:192.168.1.109:8881: database connection failed (Protocol not available)
scp cliu8private.key cliu8certificate.pem cacertificate.pem [email protected]:~/certs
[root@localhost certs]# ovs-vsctl --db=ssl:192.168.1.109:8881 --private-key=cliu8private.key --certificate=cliu8certificate.pem --ca-cert=cacertificate.pem show
2028eafc-e1db-4ea8-b0fc-30a21fdaca0f
Manager "pssl:8881"
ovs_version: "2.0.0"
NOTE:
PKI configuration (required to use SSL):
-p, --private-key=FILE file with private key
-c, --certificate=FILE file with certificate for private key
-C, --ca-cert=FILE file with peer CA certificate
Bridge 即网桥,但在 Linux 的语义中 Bridge 与以太网交换机具有相同的含义,是 Open vSwitch 最核心的对象。关于 Bridge 的概念不再多谈,更详细的内容可以浏览《Networking 基本术语/概念》一文。下面主要记录了 OvS 关于 Bridge 的常见操作。
Bridge commands:
add-br BRIDGE create a new bridge named BRIDGE
add-br BRIDGE PARENT VLAN create new fake BRIDGE in PARENT on VLAN
del-br BRIDGE delete BRIDGE and all of its ports
list-br print the names of all the bridges
br-exists BRIDGE exit 2 if BRIDGE does not exist
br-to-vlan BRIDGE print the VLAN which BRIDGE is on
br-to-parent BRIDGE print the parent of BRIDGE
br-set-external-id BRIDGE KEY VALUE set KEY on BRIDGE to VALUE
br-set-external-id BRIDGE KEY unset KEY on BRIDGE
br-get-external-id BRIDGE KEY print value of KEY on BRIDGE
br-get-external-id BRIDGE list key-value pairs on BRIDGE
# Bridge 管理操作
ovs-vsctl show
ovs-vsctl add-br
ovs-vsctl del-br
ovs-vsctl --if-exists del-br
ovs-vsctl add-port |
ovs-vsctl del-port |
ovs-vsctl list ||
ovs-vsctl list bridge
# 创建 Bridge 的同时为 Bridge 添加 Port/Interface
ovs−vsctl add−br -- add−port |
[root@localhost ~]# ovs-vsctl add-br ubuntu_br
[root@localhost ~]# ovs-vsctl show
2028eafc-e1db-4ea8-b0fc-30a21fdaca0f
Manager "ptcp:8881"
Bridge ubuntu_br
Port ubuntu_br
Interface ubuntu_br
type: internal
ovs_version: "2.0.0"
[root@localhost ~]# ip link add first_br type veth peer name first_if
[root@localhost ~]# ip link add second_br type veth peer name second_if
[root@localhost ~]# ip link add third_br type veth peer name third_if
[root@localhost ~]# ip l
1: lo: mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eno16777736: mtu 1500 qdisc pfifo_fast state UP mode DEFAULT qlen 1000
link/ether 00:0c:29:a2:2e:a4 brd ff:ff:ff:ff:ff:ff
3: ovs-system: mtu 1500 qdisc noop state DOWN mode DEFAULT qlen 1000
link/ether f6:66:5f:f9:ba:17 brd ff:ff:ff:ff:ff:ff
4: ubuntu_br: mtu 1500 qdisc noqueue state UNKNOWN mode DEFAULT qlen 1000
link/ether 46:ca:9f:ed:0b:46 brd ff:ff:ff:ff:ff:ff
5: virbr0: mtu 1500 qdisc noqueue state UP mode DEFAULT qlen 1000
link/ether 52:54:00:69:fc:47 brd ff:ff:ff:ff:ff:ff
6: virbr0-nic: mtu 1500 qdisc pfifo_fast master virbr0 state DOWN mode DEFAULT qlen 1000
link/ether 52:54:00:69:fc:47 brd ff:ff:ff:ff:ff:ff
7: vnet0: mtu 1500 qdisc pfifo_fast master virbr0 state UNKNOWN mode DEFAULT qlen 1000
link/ether fe:54:00:b8:9a:cf brd ff:ff:ff:ff:ff:ff
8: vnet1: mtu 1500 qdisc pfifo_fast master virbr0 state UNKNOWN mode DEFAULT qlen 1000
link/ether fe:54:00:43:07:06 brd ff:ff:ff:ff:ff:ff
9: vnet2: mtu 1500 qdisc pfifo_fast master virbr0 state UNKNOWN mode DEFAULT qlen 1000
link/ether fe:54:00:ce:e6:40 brd ff:ff:ff:ff:ff:ff
10: first_if@first_br: mtu 1500 qdisc noop state DOWN mode DEFAULT qlen 1000
link/ether 46:df:83:a6:66:bc brd ff:ff:ff:ff:ff:ff
11: first_br@file_if: mtu 1500 qdisc noop state DOWN mode DEFAULT qlen 1000
link/ether 72:90:6d:0d:4b:e0 brd ff:ff:ff:ff:ff:ff
12: second_if@second_br: mtu 1500 qdisc noop state DOWN mode DEFAULT qlen 1000
link/ether 6a:68:8f:38:86:a2 brd ff:ff:ff:ff:ff:ff
13: second_br@second_if: mtu 1500 qdisc noop state DOWN mode DEFAULT qlen 1000
link/ether 86:8d:e3:32:03:b4 brd ff:ff:ff:ff:ff:ff
14: third_if@third_br: mtu 1500 qdisc noop state DOWN mode DEFAULT qlen 1000
link/ether 5e:72:4c:9b:4d:18 brd ff:ff:ff:ff:ff:ff
15: third_br@third_if: mtu 1500 qdisc noop state DOWN mode DEFAULT qlen 1000
link/ether a2:cf:5f:72:da:23 brd ff:ff:ff:ff:ff:ff
[root@localhost ~]# ovs-vsctl add-port ubuntu_br first_br
[root@localhost ~]# ovs-vsctl add-port ubuntu_br second_br
[root@localhost ~]# ovs-vsctl add-port ubuntu_br third_br
[root@localhost ~]# ovs-vsctl show
2028eafc-e1db-4ea8-b0fc-30a21fdaca0f
Manager "ptcp:8881"
Bridge ubuntu_br
Port ubuntu_br
Interface ubuntu_br
type: internal
Port first_br
Interface first_br
Port second_br
Interface second_br
Port third_br
Interface third_br
ovs_version: "2.0.0"
NOTE:Bridge ubuntu_br 的同名 Port,一般就是 Bridge 的管理端口了,类比物理交换机的管理端口,一般会为其配置 IP 地址。
OvS 是一个 OpenFlow Switch 实现,可以通过 OpenFlow Controller(OpenFlow 协议)对所有分布式的 Bridge 进行统一管理。所谓 “管理”,实际上是对 Bridge 的 Flow Table 的管理。SDN Controller 的核心是控制策略的下发,并以此来决策数据的流向。
Controller 有两种类型:
OvS 为 Bridge 提供了下列两种 Fail Mode:
# 查询 Bridge 的 Fail Mode
ovs-vsctl get-fail-mode ovs-br
# 设置 Bridge 的 Fail Mode
ovs-vsctl set-fail-mode ovs-br standalone
ovs-vsctl set-fail-mode ovs-br secure
# 移除 Bridge 的 Fail Mode
ovs-vsctl del-fail-mode ovs-br
# 查看 Bridge 和 Controller 的连接模式
ovs-vsctl get controller ovs-br connection-mode
# 设置 Out-of-band 连接模式
ovs-vsctl set controller ovs-br connection-mode=out-of-band
# 设置 In-band (default) 连接模式
ovs-vsctl set controller ovs-br connection-mode=in-band
# 移除 hidden flow
ovs-vsctl set bridge br0 other-config:disable-in-band=true
# 设置 Controller
ovs-vsctl set-controller tcp::6633
# 设置 Multi Controller
ovs-vsctl set-controller tcp::6633 tcp::6633
# 获取 Bridge 的 Controller
ovs-vsctl get-controller
# 移除 Controller
ovs-vsctl del-controller
OpenFlow Controller 的种类繁多,常见有如 OpenDaylight 等等,详细清单可浏览《OpenFlow Controllers in GENI》。本篇以 Floodlight 为例,感受 Controller 对 Bridge 的流量控制功能。
官方网站:http://www.projectfloodlight.org/getting-started/
官方部署手册:https://floodlight.atlassian.net/wiki/spaces/floodlightcontroller/pages/1343544/Installation+Guide
Installing Floodlight:
git clone git://github.com/floodlight/floodlight.git
cd floodlight/
git submodule init
git submodule update
ant
sudo mkdir /var/lib/floodlight
sudo mkdir /var/log/floodlight
sudo chmod 777 /var/lib/floodlight
NOTE: 这里使用的是 Tag 1.2 版本
Running Floodlight in the Terminal:
nohup java -jar ~/floodlight/target/floodlight.jar > /var/log/floodlight/floodlight.log 2>&1 &
检查 Controller 的监听端口:
[root@localhost ~]# cat /var/log/floodlight/floodlight.log | grep "Listening for switch connections"
09:28:16.102 INFO [n.f.c.i.OFSwitchManager:main] Listening for switch connections on /0.0.0.0:6653
[root@localhost ~]# netstat -lpntu | grep 6653
tcp6 0 0 :::6653 :::* LISTEN 20159/java
NOTE:Floodlight 会通过监听 /0.0.0.0:6653
Socket 来获取 Bridge 的连接。
将 Bridge 连接到 Controller:
[root@localhost floodlight-1.2]# ovs-vsctl set-controller ubuntu_br tcp:192.168.1.109:6653
[root@localhost floodlight-1.2]# ovs-vsctl show
2028eafc-e1db-4ea8-b0fc-30a21fdaca0f
Manager "ptcp:8881"
Bridge ubuntu_br
Controller "tcp:192.168.1.109:6633"
Port ubuntu_br
Interface ubuntu_br
type: internal
Port third_br
Interface third_br
Port first_br
Interface first_br
Port second_br
Interface second_br
ovs_version: "2.0.0"
NOTE:Bridge 连接到 Controller 之后,Controller 就可以收集、下发、管理 Bridge 的相关信息了(e.g. Flow Table)。
访问 Web GUI:
http://192.168.1.109:8080/ui/index.html
将三台 KVM 虚拟机接入到 Bridge:
[root@localhost ~]# virsh domiflist VM1
Interface Type Source Model MAC
-------------------------------------------------------
macvtap1 direct ubuntu_br rtl8139 52:54:00:b8:9a:cf
[root@localhost ~]# virsh domiflist VM2
Interface Type Source Model MAC
-------------------------------------------------------
macvtap0 direct ubuntu_br rtl8139 52:54:00:43:07:06
[root@localhost ~]# virsh domiflist VM3
Interface Type Source Model MAC
-------------------------------------------------------
macvtap2 direct ubuntu_br rtl8139 52:54:00:ce:e6:40
NOTE:VM1、2、3 都连接到同一个 Bridge 上,只要三者具有网段的 IP 地址就可以通信了。
查看 Floodlight Dashboard:
等待一段时间 Floodlight 就会将会主机上的 Bridges、Bridge 上的 Hosts、Host 的 IP/MAC 地址等信息收集上来。
Floodlight Dashboard 基本上只作为展示,所以大部分操作依旧需要通过指令行来完成。
curl http://localhost:8080/wm/core/controller/switches/json
curl http://localhost:8080/wm/staticflowentrypusher/list/all/json
curl http://192.168.1.109:8080/wm/core/switch/00:00:46:ca:9f:ed:0b:46/flow/json
curl -d '{"switch": "00:00:00:00:00:00:00:01", "name":"flow-mod-1", "cookie":"0", "priority":"32768", "ingress-port":"1", "active":"true", "actions":"output=2"}' http://localhost:8080/wm/staticflowentrypusher/json
# VM1
curl -d '{"switch": "00:00:46:ca:9f:ed:0b:46", "name":"static-flow2", "cookie":"0", "priority":"32768", "src-mac":"52:54:00:b8:9a:cf","active":"true", "actions":"output=10"}' http://localhost:8080/wm/staticflowentrypusher/json
# VM3
curl -d '{"switch": "00:00:46:ca:9f:ed:0b:46", "name":"static-flow1", "cookie":"0", "priority":"32768", "src-mac":"52:54:00:ce:e6:40","active":"true", "actions":"output=12"}' http://localhost:8080/wm/staticflowentrypusher/json
curl -X DELETE -d '{"name":"flow-mod-1"}' http://localhost:8080/wm/staticflowentrypusher/json
curl http://localhost:8080/wm/staticflowentrypusher /clear//json
Mirror 的功能就是配置一个 Bridge 将某些具有特定条件的包发给指定的 Mirrored Ports。
包的条件有以下几种:
指定的目的 Ports 有以下两种:
Source (SPAN) port - A port that is monitored with use of the SPAN feature.
Destination (SPAN) port - A port that monitors source ports, usually where a network analyzer is connected.
ovs-vsctl add-br ovs-br
ovs-vsctl add-port ovs-br eth0
ovs-vsctl add-port ovs-br eth1
# 设置 SPAN Mirrors,将 ovs-br 上 add-port {eth0,eth1} Mirror 至 tap0
ovs-vsctl add-port ovs-br tap0 \
-- --id=@p get port tap0 \
-- --id=@m create mirror name=m0 select-all=true output-port=@p \
-- set bridge ovs-br mirrors=@m
# 删除 SPAN Mirrors
ovs-vsctl clear bridge ovs-br mirrors
Port 即 Bridge 的端口,每个 Port 都隶属于一个 Bridge。Interface 则是连接到 Port 的网络接口设备,实际负责接受和发送数据包。通常情况下,Port 和 Interface 是一对一关系,为 Port 配置 bond 模式后,Port:Interface 是 1:N 的关系。
Port 具有以下 3 种类型:
# 将物理网卡接口 eth1 加入 Bridge br-ex
# br-ex 会自动创建同名的 Port eth1
ovs-vsctl add-port br-ext eth1
# 创建一个 Port p0
[root@ovs ~]# ovs-vsctl add-port ovs-switch p0 -- set Interface p0 ofport_request=100
# 设定 Port p0 的 Interface p0 为 Internal 类型
[root@ovs ~]# ovs-vsctl set Interface p0 type=internal
# OvS 自动创建 Interface p0 设备
[root@ovs ~]# ip a
...
5: p0: mtu 1500 qdisc noqueue state UNKNOWN qlen 1000
link/ether 5a:d7:49:85:d9:da brd ff:ff:ff:ff:ff:ff
inet6 fe80::58d7:49ff:fe85:d9da/64 scope link
valid_lft forever preferred_lft forever
ovs-vsctl add-br br0
ovs-vsctl add-br br1
# 使用 Patch Port 连接两个 Bridge
ovs-vsctl \
-- add-port br0 patch0 -- set interface patch0 type=patch options:peer=patch1 \
-- add-port br1 patch1 -- set interface patch1 type=patch options:peer=patch0
# Host A,192.168.7.21
ovs-vsctl add-br br-vxlan
# Host B,192.168.7.23
ovs-vsctl add-br br-vxlan
# Host A 上添加连接到 Host B 的 Tunnel Port
ovs-vsctl add-port br-vxlan tun0 -- set Interface tun0 type=vxlan options:remote_ip=192.168.7.23
# Host B 上添加连接到 Host A 的 Tunnel Port
ovs-vsctl add-port br-vxlan tun0 -- set Interface tun0 type=vxlan options:remote_ip=192.168.7.21
netdev:通用网卡设备(e.g. eth0、veth)
internal:虚拟网卡设备
gre device:隧道设备(不管用户态创建多少个 GRE Tunnel,在内核态有且只有一个 GRE 设备)
Port 的一个重要的特性就是 VLAN Configuration,具有两种类型:
Trunk Port:
简而言之,就是物理交换机的 Trunk 口,符合 Trunks 列表的 VLAN ID 可通过。
Access Port:
简而言之,由 Access Port 接收到的包会被打上 Tag,Access Port 只接收 Untag 包,否则丢弃。具有相同 VLAN ID 的包会给转发到对应的 Access Port 然后解除 Tag 再发出。
# 添加 Port 并设置 VLAN tag,ID 为 3
ovs-vsctl add-port tag=3 -- set interface type=internal
# 为已存在的 Port 设置 VLAN tag,ID 为 9
ovs-vsctl set port tag=9
# 移除 VLAN tag
ovs-vsctl del-port
# 查询 VLAN
ifconfig
# 设置 Vlan Trunk
ovs-vsctl add-port trunk=3,4,5,6
# 添加设置 VLAN 的 Flow,ID 为 100
ovs-ofctl add-flow in_port=1,dl_vlan=0xffff,actions=mod_vlan_vid:100,output:3
ovs-ofctl add-flow in_port=1,dl_vlan=0xffff,actions=push_vlan:0x8100,set_field:100-\>vlan_vid,output:3
# 添加摘除 VLAN tag 的 Flow
ovs-ofctl add-flow in_port=3,dl_vlan=100,actions=strip_vlan,output:1
# ovs-ofctl add-flow pop-vlan
ovs-ofctl add-flow ovs-br in_port=3,dl_vlan=0xffff,actions=pop_vlan,output:1
Port 和 Interface 的关系是一对多的关系,因为 OvS 支持 Bond 功能。所谓 Bond 就是将多个 Interface “捆绑” 在一起形成一个虚拟的连接,从而实现高可用性以及高吞吐量的效果。
常见的 bond_mode 有以下几种:
PS:LACP (Link Aggregation Control Protocol,链路聚合控制协议)。
OvS 的 bond 模型:
OvS 中的 Qos 往往是和 Flow Policy 一起使用的。总所周知 QoS 有两个方向,一个是入方向(Ingress),一个是出方向(Egress)。
在 Linux 上最常用的网络 QoS 就是 TC 工具,其主要是通过 队列 的方式来实现的。
1.Classless Queuing Disciplines:默认为 pfifo_fast,是一种不把网络包分类的技术。pfifo_fast 根据网络包中的 TOS 对应的数字,在 TOS 的 priomap 中查看对应的 Band,不同的 Band 对应的不同的队列。
2. SFQ, Stochastic Fair Queuing:有很多的 FIFO 的队列,TCP Session 或者 UDP stream 会被分配到某个队列。包会 RoundRobin 的从各个队列中取出发送。这样一个 Session 就不会占据所有的流量。但不是每一个 Session 都具有一个队列,而是通过 Hash 算法,将大量的 Session 分配到有限的队列中。这样两个或若干个 Session 会共享一个队列,也有可能互相影响。因此 Hash 函数会经常改变,从而 Session 不会总是相互影响。
3. TBF, Token Bucket Filter:所有的网络包排成队列进行发送,但不是到了队头就能发送,而是需要拿到 Token 的包才能发送。Token 根据设定的速率(Rate)生成,所以即便队列很长,也会按照 Rate 进行发送。当没有包在队列中时,Token 还是以既定的速度生成,但是并非无限累积,而是到 Buckets 放满为止,篮子(buckets)的大小常用 burst/buffer/maxburst 参数来设定。Buckets 会避免下面这种情况:当长时间没有包发送的时候,积累了大量的 Token,突然来了大量的包,每个都能得到 Token,造成瞬间流量大增。
4. Classful Queuing Disciplines:其中典型的为 HTB, Hierarchical Token Bucket。
# 创建一个 HTB 的 qdisc 在 eth0 上,句柄为 1:,default 12 表示默认发送给 1:12。
tc qdisc add dev eth0 root handle 1: htb default 12
# 创建一个 root class,然后再创建几个 sub class。
# 同一个 root class 下的 sub class 可以相互借流量,如果直接不在 qdisc下面创建一个 root class,而是直接创建三个 class,他们之间是不能相互借流量的。
tc class add dev eth0 parent 1: classid 1:1 htb rate 100kbps ceil 100kbps
tc class add dev eth0 parent 1:1 classid 1:10 htb rate 30kbps ceil 100kbps
tc class add dev eth0 parent 1:1 classid 1:11 htb rate 10kbps ceil 100kbps
tc class add dev eth0 parent 1:1 classid 1:12 htb rate 60kbps ceil 100kbps
# 创建叶子 qdisc,分别为 fifo 和 sfq。
tc qdisc add dev eth0 parent 1:10 handle 20: pfifo limit 5
tc qdisc add dev eth0 parent 1:11 handle 30: pfifo limit 5
tc qdisc add dev eth0 parent 1:12 handle 40: sfq perturb 10
# 设定规则:从 IP 1.2.3.4 来的并且发送给 port 80 的包,从 1:10 走;其他从 1.2.3.4 发送来的包从 1:11 走;其他的走默认。
# 实现了限速与分流。
tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 match ip src 1.2.3.4 match ip dport 80 0xffff flowid 1:10
tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 match ip src 1.2.3.4 flowid 1:11
实际上 OvS 能控制的只有 Egress QoS,通过 Shaping 实现。而 Ingress QoS 是无法控制的,只能通过 Policy 将指定的包丢弃。
Ingress policy:
ovs-vsctl set Interface tap0 ingress_policing_rate=100000
ovs-vsctl set Interface tap0 ingress_policing_burst=10000
Egress shaping:Port QoS policy 仅支持 HTB。
OvS 支持三种隧道类型,这三种 Tunnel 的原理均在 《Networking 基础术语/概念》中讲过,不再赘述。
# Instance1
ovs-vsctl add-br testbr
ifconfig testbr 10.0.0.1/24
ovs-vsctl add-port testbr gre0 -- set Interface gre0 type=gre options:local_ip=192.168.100.100 options:remote_ip=192.168.100.101
ovs-vsctl add-port testbr vxlan0 -- set Interface vxlan0 type=vxlan options:local_ip=192.168.100.100 options:remote_ip=192.168.100.102
# Instance2
ovs-vsctl add-br testbr
ifconfig testbr 10.0.0.2/24
ovs-vsctl add-port testbr gre0 -- set Interface gre0 type=gre options:local_ip=192.168.100.101 options:remote_ip=192.168.100.100
ovs-vsctl add-port testbr ipsec0 -- set Interface ipsec0 type=ipsec_gre options:local_ip=192.168.100.101 options:remote_ip=192.168.100.102 options:psk=password
# Instance3
ovs-vsctl add-br testbr
ifconfig testbr 10.0.0.3/24
ovs-vsctl add-port testbr vxlan0 -- set Interface vxlan0 type=vxlan options:local_ip=192.168.100.102 options:remote_ip=192.168.100.100
ovs-vsctl add-port testbr ipsec0 -- set Interface ipsec0 type=ipsec_gre options:local_ip=192.168.100.102 options:remote_ip=192.168.100.101 options:psk=password
# enable STP,避免环导致的洪泛(Flood)
# Spanning Tree Protocol,即通过协议,将一个有环的二层网络变成一颗树。
ovs-vsctl set Bridge testbr stp_enable=true
Open vSwitch 定义了一系列的 Flow Table,通过这些 Tables 来控制网络包的流向和结构。
根据 OpenFlow 协议,一行 Flow Entry 应该由两部分组成:
Match Field 对网络包进行解析,解析的内容涵盖了 TCP/IP 协议族各层,具有下列字段,看这些字段是否能够匹配某个值。
Action 主要包含下列操作:
OvS 对 Flow Table 的管理,主要通过 ovs-ofctl 工具来完成:
# 查看 Bridge 的流表
ovs-ofctl show
# 查询指定 Bridge 的流表
ovs-ofctl dump-flows
# 添加 Bridge 的流表
ovs-ofctl add−flow
# e.g.
ovs-ofctl add-flow ovs_br dl_src=02:a2:a2:a2:a2:a2,dl_dst=02:b2:b2:b2:b2:b2,in_port=2,dl_type=0x0800,nw_src=10.0.0.1,nw_dst=10.0.0.2,actions=output:6
# 修改 Bridge 的流表
ovs-ofctl mod−flows
# 删除 Bridge 的指定流表
ovs-ofctl del−flows
# e.g.
ovs-ofctl del-flows dl_src=02:a2:a2:a2:a2:a2,dl_dst=02:b2:b2:b2:b2:b2,in_port=2,dl_type=0x0800,nw_src=10.0.0.1,nw_dst=10.0.0.2
# 删除 Bridge 的所有流表
ovs-ofctl del-flows # This will delete all the flow entries in the flow table
https://www.cnblogs.com/popsuper1982/p/3800576.html
https://blog.csdn.net/w0823m/article/details/71319973
https://blog.csdn.net/tantexian/article/details/46707175
https://opengers.github.io/openstack/openstack-base-use-openvswitch/#port