1. 架構說明
1.1. 整體架構
OVS(openvswitch)是開源的虛擬交換機。也是當前市場上雲環境中部署份額最大的交換機。支持 openflow協議,ovsdb協議管理。
一個OVS實例包括,ovsdb-server、ovs-vswitchd、datapath快轉模塊(linux內核中實現,可選的。dpdk模式是在用戶態實現快轉,並不需要內核態的datapath模塊)。
- ovsdb-server:作用是對ovsdb操作。
- ovs-vswitchd:核心模塊,作用是實現OpenFlow交換機、和controller通信、和db通信、實現用戶態轉發、和內核態快轉路徑通信。
- datapath:在內核空間實現報文快速轉發。

上圖從整體架構說明了ovs的工作方式。ovs包括ovsdb配置管理方式和openflow流表轉發控制方式。
- ovsdb配置管理方式:管理者通過OVSDB管理協議管理OVS交換機。OVSDB管理協議的規范文件是RFC 7047(The Open vSwitch Database Management Protocol)。RFC7047主要包括,定義OVSDB的結構、交互協議(JSON-RPC)、DB的操作類型。
- openflow流表控制方式:controller控制器通過OpenFlow協議給OVS交換機下發流表,控制交換機的轉發行為。controller不通過OVSDB,而是直接向OVS交換機下發流表。
ovsdb-server和ovs-vswitchd之間是通過socket交互信息。
ovs-vswitchd通過netlink和內核態快轉模塊通信。
比如通過ovs-vsctl命令增加ovs交換機接口,ovs-vsctl會通過ovsdb-server向ovsdb更新數據,ovs-vswitchd監測到ovsdb變化時,會更新交換機配置,比如添加接口。
dpdk方式的ovs除了不使用內核模塊外,架構和圖中相同。
1.3. 內部模塊及其關聯關系
為了有個整體的了解,下面列幾張整體的架構圖,不做詳細解釋,后文會有流程解釋。


2. vswitchd和ovsdb通信
IDL:接口描述語言。IDL是用來描述軟件組件接口的一種計算機語言。IDL通過一種中立的方式來描述接口,使得在不同平台上運行的對象和用不同語言編寫的程序可以相互通信交流。也就是說,用一種通用的格式,比如下面數據就是描述一個Open_vSwitch表,最后生成C語言描述的表操作.c文件
2.1. 建立連接
ovsdb_idl_create建立一個ovsdb的連接session,形如:idl =ovsdb_idl_create(remote, &ovsrec_idl_class, true, true)。
remote類似:unix:/var/run/openvswitch/db.sock。
后續使用該idl(struct ovsdb_idl)和ovsdb通信,更新數據。
2.2. 接收數據
使用jsonrpc_session_recv函數接收數據,形如:msg = jsonrpc_session_recv(idl->session)。
接收消息的結構是:
struct jsonrpc_msg {
enum jsonrpc_msg_type type;
char *method; /* Request or notification only. */
struct json *params; /* Request or notification only. */
struct json *result; /* Successful reply only. */
struct json *error; /* Error reply only. */
struct json *id; /* Request or reply only. */
};
其中type是JSON-RPC 1.0定義的消息類型,包括request、notification、reply、error:
/* Messages. */
enum jsonrpc_msg_type {
JSONRPC_REQUEST, /* Request. */
JSONRPC_NOTIFY, /* Notification.*/
JSONRPC_REPLY, /* Successfulreply. */
JSONRPC_ERROR /* Error reply. */
};
params、result、error、id是不同類型的消息對應的消息數據。
當解析消息后,監測到需要更新本地數據時,調用ovsdb_idl_parse_update函數,解析消息並更新本地數據。2.3. json數據解析
/* A JSON value. */
struct json {
enum json_type type;
union {
struct shash *object; /* Contains "struct json *"s. */
struct json_array array;
long long int integer;
double real;
char *string;
} u;
};
type字段:說明了該json數據是聯合體中的哪一種類型。
/* Type of a JSON value. */
enum json_type {
JSON_NULL, /* null */
JSON_FALSE, /* false */
JSON_TRUE, /* true */
JSON_OBJECT, /* {"a": b, "c": d, ...}*/
JSON_ARRAY, /* [1, 2, 3, ...] */
JSON_INTEGER, /* 123. */
JSON_REAL, /* 123.456. */
JSON_STRING, /* "..." */
JSON_N_TYPES
};
聯合體u:表示數據可以解析的類型。
例如,當type是JSON_OBJECT類型時,且是update2更新操作,那么object字段保存着多個數據表,每張表數據中又攜帶着待更新的多個row數據,和每row對應的操作(modify、insert、delete、initial)。然后遍歷每個表的每行數據,並執行對應的數據操作(modify、insert、delete、initial)對本地數據更新。
注:本地數據和遠端的OVSDB並不相同,本地數據是vswitchd從數據庫取出數據后臨時放到本地結構中的。
2.4. 更新數據改變標記
當監聽到ovsdb-server發來的rpc消息時,如果數據庫發送改變就改變對應的change_seqno。后續vswitchd根據change_seqno的值是否發生了變化,決定是否重新配置ovs交換機。
3. dpdk加速處理
3.1. ovs dpdk加速配置
在使用dpdk 類型的datapath加速之前,需要設置dpdk-init=true啟動參數。
設置方法:
ovs-vsctl --no-wait set Open_vSwitch .other_config:dpdk-init=true
設置dpdk的相關參數,都通過other_config選項完成。
主要的參數有:
- dpdk-init
指定ovs是否初始化和支持dpdk端口。
- dpdk-lcore-mask
指明dpdk使用的邏輯核。同dpdk的-c參數。
- dpdk-socket-mem
指明不同numa節點提前申請的大頁內存。同dpdk的--socket-mem參數。
- dpdk-hugepage-dir
大頁文件系統mount的目錄。同dpdk的--huge-dir參數。
- vhost-sock-dir
設置vhost-user 套接字的路徑。
- dpdk-extra
其他的dpdk配置參數。
3.2. dpdk初始化
主要包括:
1) dpdk eal初始化。
2) 啟動dpdk接口狀態監控線程(使用dpdk的庫函數),如果狀態發送變化,則更新netdev設備的變化標記。
3) 注冊dpdk類型的netdev class。其中包括dpdk設備類型,ring類型、vhost、vhost client類型。
4. 數據結構抽象關系
4.1. 分層抽象

- ofproto class
openflow交換機實現類,用來實現一個openflow交換機。主要包括創建、構造、操作openflowflow等相關方法。
- dpif class
datapath接口類,用來和datapath交互。主要包括datapath的open、run、端口操作、端口數據監聽等相關方法。datapath是數據面的一種抽象。
- netdev class
網絡設備抽象類,用來和網絡設備交互。主要包括網卡的設置、網卡結構的抽象、數據包發送接收等相關方法。
- ofproto class
openflow交換機實現類,用來實現一個openflow交換機。主要包括創建、構造、操作openflowflow等相關方法。
- dpif class
datapath接口類,用來和datapath交互。主要包括datapath的open、run、端口操作、端口數據監聽等相關方法。datapath是數據面的一種抽象。
- netdev class
網絡設備抽象類,用來和網絡設備交互。主要包括網卡的設置、網卡結構的抽象、數據包發送接收等相關方法。
bridge網橋通過ofproto class構造一個openflow交換機。再通過dpif class打開(如果沒有對應類型的datapath,就創建一個)對應類型的datapath通路。使用netdev class構造一個抽象網絡設備,上層抽象為端口,然后對網絡設備收發包等操作。
需要說明的是,datapath是按類型分類的,現在ovs有兩種類型的datapath:system(對應linux內核數據通路)、netdev(dpdk用戶態數據通路)。所以使用同一種datapath類型的ovs網橋共享同一個datapath對象。如下圖所示。

4.2. 主要數據結構關系
該部分說明的內容是關於dpdk類型的datapath。
注:A B,表示A是一個變量而非指針,它的結構和內容是B。
流程解釋如下:
1) bridge層
1.1 struct bridge對應每個ovs網橋最上層的結構。它通過成員指針變量ofproto,聯系到struct ofproto交換機結構內容。
2) ofproto層
2.1 由於聯系到的struct ofproto內容是struct ofproto_dpif結構的一個up成員變量。所以通過struct ofproto地址可以cast到struct ofproto_dpif結構內容,通過該內容和dpif層交互。
2.2 通過struct ofproto_dpif的成員指針變量dpif_backer,聯系到struct dpif_backer結構內容。該結構是dpif操作的入口點。
3) dpif層
3.1 通過struct dpif_backer的成員指針變量dpif,聯系到structdpif結構內容。
3.2 由於struct dpif結構內容是struct dpif_netdev結構的dpif成員變量,所以通過structdpif地址可以cast到struct dpif_netdev結構內容。
3.3 struct dpif_netdev結構內容中有指針成員變量dp,通過dp可以聯系到struct dp_netdev結構內容。該內容是datapath的抽象結構內容。
4) datapath層
4.1 struct dp_netdev的成員指針變量ports是一個表結構,它聯系着多個port端口內容。端口的結構是struct dp_netdev_port。struct dp_netdev另外一個成員指針變量netdev,聯系着dp層的struct netdev結構內容,該結構內容是網卡的邏輯抽象。
4.2 struct dp_netdev_port的指針成員變量rxqs也是一個表結構,它聯系着多個隊列內容。隊列的結構是struct dp_netdev_rxq.它的成員變量core_id表示該隊列所在的core。
4.3 通過struct dp_netdev_rxq的成員指針變量rxq,聯系到dp層的struct netdev_rxq結構內容。structdp_netdev_rxq的成員變量queue_id表示該隊列id。
5) netdev層
5.1 由於struct netdev結構內容是struct netdev_dpdk結構的一個up成員變量。所以通過struct netdev地址可以cast到struct netdev_dpdk結構內容。
5.2 通過struct detdev_dpdk結構內容的成員指針變量netdev_class和dpdk類型網卡交互。
5.3 由於struct netdev_rxq結構內容是struct netdev_rxq_dpdk結構內容的一個up成員變量。所以通過struct netdev_rxq地址可以cast到struct netdev_rxq_dpdk結構內容。該內容是dpdk類型的netdev_rxq,它的成員變量port_id是dpdk網絡設備的port。
5. vswitchd啟動
簡要的概括下vswitchd啟動的內容。
1) 和ovsdb-server通信初始化,建立
2) dpdk初始化
3) ofproto(OpenFlow交換機)初始化
4) netdev初始化
5) 網橋配置
更新網橋(添加、刪除)
更新端口
創建ofproto(OpenFlow交換機),創建或打開dpif_backer
添加端口,添加到pmd中
配置網橋、端口屬性
datapath run
dp_netdev的pmd更新
dpif_run(運行dpif-netdev的datapath,配置netdev等,接收發送報文)
ofproto run,配置ofproto需要的參數和功能(netflow、sflow、ipfix、mac學習)
OpenFlow控制器連接相關run
6. 附錄-數據結構查詢
6.1. ofproto_class
是一個OpenFlow交換機的實現類結構,提供的類功能很多,主要分為:
1. 工廠函數功能。如初始化、枚舉支持的datapath類型。
2. datapath類型處理。
3. OpenFlow交換機功能。如構造、運行等。
4. OpenFlow流表功能。
5. OpenFlow配置。
6. OpenFlow計量。
7. OpenFlow 1.1 group功能。
8. 獲取datapath信息功能。
9. 連接表功能。
6.2. dpif_class
datapath接口實現類,每種datapath都有對應的接口實現實例。dpif有兩種實現類:dpif_netlink_class、dpif_netdev_class。即system、netdev。比如,dpdk的netdevdatapath類型的實現實例dpif_netdev_class。
6.3. netdev_class
網絡設備實現類。包括抽象的設備結構和收發包功能。
6.4. bridge
6.5. ofproto
6.6. port
6.7. iface
6.8. ofport
6.9. ofport_dpif
6.10. ofproto_port
6.11. dpif_port
dp向dpif返回的端口信息。
6.12. ofproto_dpif
6.13. dpif_backer
datapath類型共享實現實例。
6.14. udpif
6.15. dpif
datapath接口。
6.16. dpif_netdev
netdev類型的datapath的接口。
6.17. dp_netdev
基於網絡設備接口的datapath。
6.18. dp_netdev_port
基於netdev類型的datapath的端口。
掛載在dp_netdev的ports成員中。
6.19. netdev
抽象的網絡設備。
6.20. netdev_dpdk
dpdk類型的netdev結構,繼承自netdev結構。
6.21. dp_netdev_rxq
用來表示core和隊列的對應關系。
6.22. netdev_rxq
網絡設備的收包隊列結構,以隊列區分。
6.23. netdev_rxq_dpdk
dpdk的收包隊列結構,繼承自netdev_rxq.
端口號和netdev_dpdk中的端口號相同。
使用dpdk的接口函數rte_zmalloc申請,受dpdk的內存管理控制。
6.24. dp_netdev_pmd_thread
6.25. rxq_poll
6.26. tx_port
OVS 架構
OVS 是產品級的虛擬交換機,大量應用在生產環境中,支撐整個數據中心虛擬網絡的運轉。OVS 基於 SDN 的思想,將整個核心架構分為控制面和數據面,數據面負責數據的交換工作,控制面實現交換策略,指導數據面工作。
從整體上看,OVS 可以划分為三大塊,管理面、數據面和控制面。
數據面就是以用戶態的 ovs-vswitchd 和內核態的 datapath 為主的轉發模塊,以及與之相關聯的數據庫模塊 ovsdb-server,控制面主要是由 ovs-ofctl 模塊負責,基於 OpenFlow 協議與數據面進行交互。而管理面則是由 OVS 提供的各種工具來負責,這些工具的提供也是為了方便用戶對底層各個模塊的控制管理,提高用戶體驗。下面就對這些工具進行一個逐一的闡述。
ovs-ofctl
:這個是控制面的模塊,但本質上它也是一個管理工具,主要是基於 OpenFlow 協議對 OpenFlow 交換機進行監控和管理,通過它可以顯示一個 OpenFlow 交換機的當前狀態,包括功能、配置和表中的項。使用時,有很多參數,我們可以通過 ovs-ofctl --help 查看。
-
常用命令:
-
-
ovs-ofctl show switch-name :輸出交換機信息,包括其流量表和端口信息。
-
-
ovs-ofctl dump-ports switch-name:輸出交換機的端口統計信息,包括收發包、丟包、錯誤包等數量。
-
-
ovs-ofctl add-flow switch-name:為交換機配置流策略。
ovs-dpctl
:用來配置交換機的內核模塊 datapath,它可以創建,修改和刪除 datapath,一般,單個機器上的 datapath 有 256 條(0-255)。一條 datapath 對應一個虛擬網絡設備。該工具還可以統計每條 datapath 上的設備通過的流量,打印流的信息等,更過參數通過 ovs-dpctl --help 查看。
-
常用命令:
-
-
ovs-dpctl show :顯示所有 datapath 的基本信息。
-
-
ovs-dpctl dump-dps :顯示所有 datapath 的名字。
-
-
ovs-dpctl dump-flows DP :顯示一條 datapath DP 上的流信息。
ovs-appctl
:查詢和控制運行中的 OVS 守護進程,包括 ovs-switchd,datapath,OpenFlow 控制器等,兼具 ovs-ofctl、ovs-dpctl 的功能,是一個非常強大的命令。ovs-vswitchd 等進程啟動之后就以一個守護進程的形式運行,為了能夠很好的讓用戶控制這些進程,就有了這個命令。詳細可以 ovs-appctl --help 查看。
ovs-vsctl
:查詢和更新 ovs-vswitchd 的配置,這也是一個很強大的命令,網橋、端口、協議等相關的命令都由它來完成。此外,還負責和 ovsdb-server 相關的數據庫操作。
-
常用命令:
-
-
ovs-vsctl show :顯示主機上已有的網橋及端口信息。
-
-
ovs-vsctl add-br br0:添加網橋 br0。
ovsdb-client
:訪問 ovsdb-server 的客戶端程序,通過 ovsdb-server 執行一些數據庫操作。
-
常用命令:
-
-
ovsdb-client dump:用來查看ovsdb內容。
-
-
ovsdb-client transact :用來執行一條類 sql。
ovsdb-tool
:和 ovsdb-client 要借助 ovsdb-server 才能進行相關數據庫操作不同,ovsdb-tool 可以直接操作數據庫。
OVS 源碼結構
OVS 源碼結構中,主要包含以下幾個主要的模塊,數據交換邏輯在 vswitchd 和 datapath 中實現,vswitchd 是最核心的模塊,OpenFlow 的相關邏輯都在 vswitchd 中實現,datapath 則不是必須的模塊。ovsdb 用於存儲 vswitch 本身的配置信息,如端口、拓撲、規則等。控制面部分采用的是 OVS 自家實現的 OVN,和其他控制器相比,OVN 對 OVS 和 OpenStack 有更好的兼容性和性能。
從圖中可以看出 OVS 的分層結構,最上層 vswitchd 主要與 ovsdb 通信,做配置下發和更新等,中間層是 ofproto ,用於和 OpenFlow 控制器通信,並基於下層的 ofproto provider 提供的接口,完成具體的設備操作和流表操作等工作。
dpif 層實現對流表的操作。
netdev 層實現了對網絡設備(如 Ethernet)的抽象,基於 netdev provider 接口實現多種不同平台的設備,如 Linux 內核的 system, tap, internal 等,dpdk 系的 vhost, vhost-user 等,以及隧道相關的 gre, vxlan 等。
數據轉發流程
通過一個例子來看看 OVS 中數據包是如何進行轉發的。
1)ovs 的 datapath 接收到從 ovs 連接的某個網絡端口發來的數據包,從數據包中提取源/目的 IP、源/目的 MAC、端口等信息。
2)ovs 在內核態查看流表結構(通過 hash),如果命中,則快速轉發。
3)如果沒有命中,內核態不知道如何處置這個數據包,所以,通過 netlink upcall 機制從內核態通知用戶態,發送給 ovs-vswitchd 組件處理。
4)ovs-vswitchd 查詢用戶態精確流表和模糊流表,如果還不命中,在 SDN 控制器接入的情況下,經過 OpenFlow 協議,通告給控制器,由控制器處理。
5)如果模糊命中, ovs-vswitchd 會同時刷新用戶態精確流表和內核態精確流表,如果精確命中,則只更新內核態流表。
6)刷新后,重新把該數據包注入給內核態 datapath 模塊處理。
7)datapath 重新發起選路,查詢內核流表,匹配;報文轉發,結束。
總結
OVS 為了方便用戶操作,提供了很多管理工具,我們平常在使用過程中只需記住每個工具的作用,具體的命令可以使用 -h 或 --help 查看。
創建一個OVS 交換機
創建一個叫ovs-switch的交換機
-
$ ovs-vsctl add-br ovs-switch
-
創建一個端口 p0,設置端口 p0 的 OpenFlow 端口編號為 100(如果在創建端口的時候沒有指定 OpenFlow 端口編號,OVS 會自動生成一個)。
-
$ ovs-vsctl add-port ovs-switch p0 -- set Interface p0 ofport_request=100
-
設置網絡接口設備的類型為"internal"。對於 internal 類型的的網絡接口,OVS 會同時在 Linux 系統中創建一個可以用來收發數據的模擬網絡設備。我們可以為這個網絡設備配置 IP 地址、進行數據監聽等操作。
-
$ ovs-vsctl set Interface p0 type=internal
-
$ ethtool -i p0
-
driver: openvswitch
-
version:
-
firmware-version:
-
bus-info:
-
supports-statistics: no
-
supports-test: no
-
supports-eeprom-access: no
-
supports-register-dump: no
為了避免網絡接口上的地址和本機已有網絡地址沖突,我們可以創建一個虛擬網絡空間 ns0,把 p0 接口移入網絡空間 ns0,並配置 IP 地址為 192.168.1.100
-
$ ip netns add ns0
-
$ ip link set p0 netns ns0
-
$ ip netns exec ns0 ip addr add 192.168.1.100/24 dev p0
-
$ ip netns exec ns0 ifconfig p0 promisc up
使用同樣的方法創建端口 p1、p2
創建的端口信息
端口 |
說明 |
p0 |
IP 地址: 192.168.1.100/24 |
p1 |
IP 地址: 192.168.1.101/24 |
p2 |
IP 地址: 192.168.1.102/24, |
創建所有的端口之后, 查看 OVS 交換機的信息
-
$ ovs-vsctl show
-
30282710-d401-4187-8e13-52388f693df7
-
Bridge ovs-switch
-
Port "p0"
-
Interface "p0"
-
type: internal
-
Port "p2"
-
Interface "p2"
-
type: internal
-
Port "p1"
-
Interface "p1"
-
type: internal
-
Port ovs-switch
-
Interface ovs-switch
-
type: internal
使用 ovs-ofctl 創建並測試 OpenFlow 命令
查看 Open vSwitch 中的端口信息。從輸出結果中,可以獲得交換機對應的 datapath ID (dpid),以及每個端口的 OpenFlow 端口編號,端口名稱,當前狀態等等。
-
$ ovs-ofctl show ovs-switch
-
OFPT_FEATURES_REPLY (xid= 0x2): dpid:00001232a237ea45
-
n_tables: 254, n_buffers:256
-
capabilities: FLOW_STATS TABLE_STATS PORT_STATS QUEUE_STATS ARP_MATCH_IP
-
actions: OUTPUT SET_VLAN_VID SET_VLAN_PCP STRIP_VLAN SET_DL_SRC SET_DL_DST
-
SET_NW_SRC SET_NW_DST SET_NW_TOS SET_TP_SRC SET_TP_DST ENQUEUE
-
100(p0): addr:54:01:00:00:00:00
-
config: PORT_DOWN
-
state: LINK_DOWN
-
speed: 0 Mbps now, 0 Mbps max
-
101(p1): addr:54:01:00:00:00:00
-
config: PORT_DOWN
-
state: LINK_DOWN
-
speed: 0 Mbps now, 0 Mbps max
-
102(p2): addr:54:01:00:00:00:00
-
config: PORT_DOWN
-
state: LINK_DOWN
-
speed: 0 Mbps now, 0 Mbps max
-
LOCAL(ovs-switch): addr: 12:32:a2:37:ea:45
-
config: 0
-
state: 0
-
speed: 0 Mbps now, 0 Mbps max
-
OFPT_GET_CONFIG_REPLY (xid= 0x4): frags=normal miss_send_len=0
如果想獲得網絡接口的 OpenFlow 編號,也可以在 OVS 的數據庫中查詢
-
$ ovs-vsctl get Interface p0 ofport
-
100
查看 datapath 的信息
-
$ ovs-dpctl show
-
system@ovs-system:
-
lookups: hit: 12173 missed:712 lost:0
-
flows: 0
-
port 0: ovs-system (internal)
-
port 1: ovs-switch (internal)
-
port 2: p0 (internal)
-
port 3: p1 (internal)
-
port 4: p2 (internal)
屏蔽數據包
屏蔽所有進入 OVS 的以太網廣播數據包
-
$ ovs-ofctl add-flow ovs-switch "table=0, dl_src=01:00:00:00:00:00/01:00:00:00:00:00, actions=drop"
-
屏蔽 STP 協議的廣播數據包
-
$ ovs-ofctl add-flow ovs-switch "table=0, dl_dst=01:80:c2:00:00:00/ff:ff:ff:ff:ff:f0, actions=drop"
-
修改數據包
添加新的 OpenFlow 條目,修改從端口 p0 收到的數據包的源地址為 9.181.137.1
-
$ ovs-ofctl add-flow ovs-switch "priority=1 idle_timeout=0,\
-
in_port=100,actions=mod_nw_src:9.181.137.1,normal"
$ ip netns exec ns0 ping 192.168.1.101
在接收端口 p1 監控數據,發現接收到的數據包的來源已經被修改為 9.181.137.1
-
$ ip netns exec ns1 tcpdump -i p1 icmp
-
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
-
listening on p1, link-type EN10MB (Ethernet), capture size 65535 bytes
-
15:59:16.885770 IP 9.181.137.1 > 192.168.1.101: ICMP echo request, id 23111, seq 457, length 64
-
15:59:17.893809 IP 9.181.137.1 > 192.168.1.101: ICMP echo request, id 23111, seq 458, length 64
重定向數據包
添加新的 OpenFlow 條目,重定向所有的 ICMP 數據包到端口 p2
-
$ ovs-ofctl add-flow ovs-switch idle_timeout=0,dl_type=0x0800,nw_proto=1,actions=output:102
-
從端口 p0 (192.168.1.100)發送數據到端口 p1(192.168.1.101)
-
$ ip netns exec ns0 ping 192.168.1.101
-
在端口 p2 上監控數據,發現數據包已被轉發到端口 p2
-
$ ip netns exec ns3 tcpdump -i p2 icmp
-
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
-
listening on p2, link-type EN10MB (Ethernet), capture size 65535 bytes
-
16:07:35.677770 IP 192.168.1.100 > 192.168.1.101: ICMP echo request, id 23147, seq 25, length 64
-
16:07:36.685824 IP 192.168.1.100 > 192.168.1.101: ICMP echo request, id 23147, seq 26, length 64
修改數據包的 VLAN Tag
除了使用"ping"、"tcpdump"和"iperf" 等 Linux 命令以外,我們也可以使用 OVS 提供的 ovs-appctl ofproto/trace 工具來測試 OVS 對數據包的轉發狀況。ovs-appctl ofproto/trace 可以用來生成測試用的模擬數據包,並一步步的展示 OVS 對數據包的流處理過程。在以下的例子中,我們演示一下如何使用這個命令:
修改端口 p1 的 VLAN tag 為 101,使端口 p1 成為一個隸屬於 VLAN 101 的端口
-
$ ovs-vsctl set Port p1 tag=101
-
現在由於端口 p0 和 p1 屬於不同的 VLAN,它們之間無法進行數據交換。我們使用 ovs-appctl ofproto/trace 生成一個從端口 p0 發送到端口 p1 的數據包,這個數據包不包含任何 VLAN tag,並觀察 OVS 的處理過程
在第一行輸出中,"Flow:"之后的字段描述了輸入的流的信息。由於我們沒有指定太多信息,所以多數字段 (例如 dl_type 和 vlan_tci)被 OVS 設置為空值。
在第二行的輸出中,"Rule:" 之后的字段描述了匹配成功的流表項。
在第三行的輸出中,"OpenFlow actions"之后的字段描述了實際執行的操作。
最后一段以"Final flow"開始的字段是整個處理過程的總結,"Datapath actions: 4,1"代表數據包被發送到 datapath 的 4 和 1 號端口。
-
$ ovs-appctl ofproto/trace ovs-switch in_port= 100,dl_src=66:4e:cc:ae:4d:20,
-
dl_dst= 46:54:8a:95:dd:f8 -generate
-
Flow:metadata= 0,in_port=100,vlan_tci=0x0000,dl_src=66:4e:cc:ae:4d:20,
-
dl_dst= 46:54:8a:95:dd:f8,dl_type=0x0000
-
Rule: table= 0 cookie=0 priority=0
-
OpenFlow actions=NORMAL
-
no learned MAC for destination, flooding
-
-
Final flow: unchanged
-
Relevant fields: skb_priority= 0,in_port=100,vlan_tci=0x0000/0x1fff,\
-
dl_src= 66:4e:cc:ae:4d:20,dl_dst=46:54:8a:95:dd:f8,dl_type=0x0000,nw_frag=no
-
Datapath actions: 4,1
創建一條新的 Flow:對於從端口 p0 進入交換機的數據包,如果它不包含任何 VLAN tag,則自動為它添加 VLAN tag 101
-
$ ovs-ofctl add-flow ovs-switch "priority=3,in_port=100,dl_vlan=0xffff,\
-
actions=mod_vlan_vid:101,normal"
再次嘗試從端口 p0 發送一個不包含任何 VLAN tag 的數據包,發現數據包進入端口 p0 之后, 會被加上 VLAN tag101, 同時轉發到端口 p1 上
-
$ ovs-appctl ofproto/trace ovs-switch in_port= 100,dl_src=66:4e:cc:ae:4d:20,
-
dl_dst= 46:54:8a:95:dd:f8 –generate
-
Flow: metadata= 0,in_port=100,vlan_tci=0x0000,dl_src=66:4e:cc:ae:4d:20,
-
dl_dst= 46:54:8a:95:dd:f8,dl_type=0x0000
-
Rule: table= 0 cookie=0 priority=3,in_port=100,vlan_tci=0x0000
-
OpenFlow actions=mod_vlan_vid: 101,NORMAL
-
forwarding to learned port
-
-
Final flow: metadata= 0,in_port=100,dl_vlan=101,dl_vlan_pcp=0,dl_src=66:4e:cc:ae:4d:20,
-
dl_dst= 46:54:8a:95:dd:f8,dl_type=0x0000
-
Relevant fields: skb_priority= 0,in_port=100,vlan_tci=0x0000/0x1fff,dl_src=66:4e:cc:ae:4d:20,
-
dl_dst= 46:54:8a:95:dd:f8,dl_type=0x0000,nw_frag=no
-
Datapath actions: 3
反過來從端口 p1 發送數據包,由於 p1 現在是帶有 VLAN tag 101 的 Access 類型的端口,所以數據包進入端口 p1 之后,會被 OVS 添加 VLAN tag 101 並發送到端口 p0
$ ovs-appctl ofproto/trace ovs-switch in_port=101,dl_dst=66:4e:cc:ae:4d:20,
-
dl_src= 46:54:8a:95:dd:f8 -generate
-
Flow: metadata= 0,in_port=101,vlan_tci=0x0000,dl_src=46:54:8a:95:dd:f8,
-
dl_dst= 66:4e:cc:ae:4d:20,dl_type=0x0000
-
Rule: table= 0 cookie=0 priority=0
-
OpenFlow actions=NORMAL
-
forwarding to learned port
-
-
Final flow: unchanged
-
Relevant fields: skb_priority= 0,in_port=101,vlan_tci=0x0000,dl_src=46:54:8a:95:dd:f8,
-
dl_dst= 66:4e:cc:ae:4d:20,dl_type=0x0000,nw_frag=no
-
Datapath actions: push_vlan(vid= 101,pcp=0),2
其他 OpenFlow 常用的操作
查看交換機中的所有 Table
ovs-ofctl dump-tables ovs-switch
查看交換機中的所有流表項
ovs-ofctl dump-flows ovs-switch
刪除編號為 100 的端口上的所有流表項
ovs-ofctl del-flows ovs-switch "in_port=100"
查看交換機上的端口信息
ovs-ofctl show ovs-switch
OVS架構解析--dpdk datapath數據通路
1. 架構說明
1.1. 整體架構
OVS(openvswitch)是開源的虛擬交換機。也是當前市場上雲環境中部署份額最大的交換機。支持 openflow協議,ovsdb協議管理。
一個OVS實例包括,ovsdb-server、ovs-vswitchd、datapath快轉模塊(linux內核中實現,可選的。dpdk模式是在用戶態實現快轉,並不需要內核態的datapath模塊)。
- ovsdb-server:作用是對ovsdb操作。
- ovs-vswitchd:核心模塊,作用是實現OpenFlow交換機、和controller通信、和db通信、實現用戶態轉發、和內核態快轉路徑通信。
- datapath:在內核空間實現報文快速轉發。

上圖從整體架構說明了ovs的工作方式。ovs包括ovsdb配置管理方式和openflow流表轉發控制方式。
- ovsdb配置管理方式:管理者通過OVSDB管理協議管理OVS交換機。OVSDB管理協議的規范文件是RFC 7047(The Open vSwitch Database Management Protocol)。RFC7047主要包括,定義OVSDB的結構、交互協議(JSON-RPC)、DB的操作類型。
- openflow流表控制方式:controller控制器通過OpenFlow協議給OVS交換機下發流表,控制交換機的轉發行為。controller不通過OVSDB,而是直接向OVS交換機下發流表。
ovsdb-server和ovs-vswitchd之間是通過socket交互信息。
ovs-vswitchd通過netlink和內核態快轉模塊通信。
比如通過ovs-vsctl命令增加ovs交換機接口,ovs-vsctl會通過ovsdb-server向ovsdb更新數據,ovs-vswitchd監測到ovsdb變化時,會更新交換機配置,比如添加接口。
dpdk方式的ovs除了不使用內核模塊外,架構和圖中相同。
1.3. 內部模塊及其關聯關系
為了有個整體的了解,下面列幾張整體的架構圖,不做詳細解釋,后文會有流程解釋。


2. vswitchd和ovsdb通信
IDL:接口描述語言。IDL是用來描述軟件組件接口的一種計算機語言。IDL通過一種中立的方式來描述接口,使得在不同平台上運行的對象和用不同語言編寫的程序可以相互通信交流。也就是說,用一種通用的格式,比如下面數據就是描述一個Open_vSwitch表,最后生成C語言描述的表操作.c文件
2.1. 建立連接
ovsdb_idl_create建立一個ovsdb的連接session,形如:idl =ovsdb_idl_create(remote, &ovsrec_idl_class, true, true)。
remote類似:unix:/var/run/openvswitch/db.sock。
后續使用該idl(struct ovsdb_idl)和ovsdb通信,更新數據。
2.2. 接收數據
使用jsonrpc_session_recv函數接收數據,形如:msg = jsonrpc_session_recv(idl->session)。
接收消息的結構是:
struct jsonrpc_msg {
enum jsonrpc_msg_type type;
char *method; /* Request or notification only. */
struct json *params; /* Request or notification only. */
struct json *result; /* Successful reply only. */
struct json *error; /* Error reply only. */
struct json *id; /* Request or reply only. */
};
其中type是JSON-RPC 1.0定義的消息類型,包括request、notification、reply、error:
/* Messages. */
enum jsonrpc_msg_type {
JSONRPC_REQUEST, /* Request. */
JSONRPC_NOTIFY, /* Notification.*/
JSONRPC_REPLY, /* Successfulreply. */
JSONRPC_ERROR /* Error reply. */
};
params、result、error、id是不同類型的消息對應的消息數據。
當解析消息后,監測到需要更新本地數據時,調用ovsdb_idl_parse_update函數,解析消息並更新本地數據。2.3. json數據解析
/* A JSON value. */
struct json {
enum json_type type;
union {
struct shash *object; /* Contains "struct json *"s. */
struct json_array array;
long long int integer;
double real;
char *string;
} u;
};
type字段:說明了該json數據是聯合體中的哪一種類型。
/* Type of a JSON value. */
enum json_type {
JSON_NULL, /* null */
JSON_FALSE, /* false */
JSON_TRUE, /* true */
JSON_OBJECT, /* {"a": b, "c": d, ...}*/
JSON_ARRAY, /* [1, 2, 3, ...] */
JSON_INTEGER, /* 123. */
JSON_REAL, /* 123.456. */
JSON_STRING, /* "..." */
JSON_N_TYPES
};
聯合體u:表示數據可以解析的類型。
例如,當type是JSON_OBJECT類型時,且是update2更新操作,那么object字段保存着多個數據表,每張表數據中又攜帶着待更新的多個row數據,和每row對應的操作(modify、insert、delete、initial)。然后遍歷每個表的每行數據,並執行對應的數據操作(modify、insert、delete、initial)對本地數據更新。
注:本地數據和遠端的OVSDB並不相同,本地數據是vswitchd從數據庫取出數據后臨時放到本地結構中的。
2.4. 更新數據改變標記
當監聽到ovsdb-server發來的rpc消息時,如果數據庫發送改變就改變對應的change_seqno。后續vswitchd根據change_seqno的值是否發生了變化,決定是否重新配置ovs交換機。
3. dpdk加速處理
3.1. ovs dpdk加速配置
在使用dpdk 類型的datapath加速之前,需要設置dpdk-init=true啟動參數。
設置方法:
ovs-vsctl --no-wait set Open_vSwitch .other_config:dpdk-init=true
設置dpdk的相關參數,都通過other_config選項完成。
主要的參數有:
- dpdk-init
指定ovs是否初始化和支持dpdk端口。
- dpdk-lcore-mask
指明dpdk使用的邏輯核。同dpdk的-c參數。
- dpdk-socket-mem
指明不同numa節點提前申請的大頁內存。同dpdk的--socket-mem參數。
- dpdk-hugepage-dir
大頁文件系統mount的目錄。同dpdk的--huge-dir參數。
- vhost-sock-dir
設置vhost-user 套接字的路徑。
- dpdk-extra
其他的dpdk配置參數。
3.2. dpdk初始化
主要包括:
1) dpdk eal初始化。
2) 啟動dpdk接口狀態監控線程(使用dpdk的庫函數),如果狀態發送變化,則更新netdev設備的變化標記。
3) 注冊dpdk類型的netdev class。其中包括dpdk設備類型,ring類型、vhost、vhost client類型。
4. 數據結構抽象關系
4.1. 分層抽象

- ofproto class
openflow交換機實現類,用來實現一個openflow交換機。主要包括創建、構造、操作openflowflow等相關方法。
- dpif class
datapath接口類,用來和datapath交互。主要包括datapath的open、run、端口操作、端口數據監聽等相關方法。datapath是數據面的一種抽象。
- netdev class
網絡設備抽象類,用來和網絡設備交互。主要包括網卡的設置、網卡結構的抽象、數據包發送接收等相關方法。
- ofproto class
openflow交換機實現類,用來實現一個openflow交換機。主要包括創建、構造、操作openflowflow等相關方法。
- dpif class
datapath接口類,用來和datapath交互。主要包括datapath的open、run、端口操作、端口數據監聽等相關方法。datapath是數據面的一種抽象。
- netdev class
網絡設備抽象類,用來和網絡設備交互。主要包括網卡的設置、網卡結構的抽象、數據包發送接收等相關方法。
bridge網橋通過ofproto class構造一個openflow交換機。再通過dpif class打開(如果沒有對應類型的datapath,就創建一個)對應類型的datapath通路。使用netdev class構造一個抽象網絡設備,上層抽象為端口,然后對網絡設備收發包等操作。
需要說明的是,datapath是按類型分類的,現在ovs有兩種類型的datapath:system(對應linux內核數據通路)、netdev(dpdk用戶態數據通路)。所以使用同一種datapath類型的ovs網橋共享同一個datapath對象。如下圖所示。

4.2. 主要數據結構關系
該部分說明的內容是關於dpdk類型的datapath。
注:A B,表示A是一個變量而非指針,它的結構和內容是B。
流程解釋如下:
1) bridge層
1.1 struct bridge對應每個ovs網橋最上層的結構。它通過成員指針變量ofproto,聯系到struct ofproto交換機結構內容。
2) ofproto層
2.1 由於聯系到的struct ofproto內容是struct ofproto_dpif結構的一個up成員變量。所以通過struct ofproto地址可以cast到struct ofproto_dpif結構內容,通過該內容和dpif層交互。
2.2 通過struct ofproto_dpif的成員指針變量dpif_backer,聯系到struct dpif_backer結構內容。該結構是dpif操作的入口點。
3) dpif層
3.1 通過struct dpif_backer的成員指針變量dpif,聯系到structdpif結構內容。
3.2 由於struct dpif結構內容是struct dpif_netdev結構的dpif成員變量,所以通過structdpif地址可以cast到struct dpif_netdev結構內容。
3.3 struct dpif_netdev結構內容中有指針成員變量dp,通過dp可以聯系到struct dp_netdev結構內容。該內容是datapath的抽象結構內容。
4) datapath層
4.1 struct dp_netdev的成員指針變量ports是一個表結構,它聯系着多個port端口內容。端口的結構是struct dp_netdev_port。struct dp_netdev另外一個成員指針變量netdev,聯系着dp層的struct netdev結構內容,該結構內容是網卡的邏輯抽象。
4.2 struct dp_netdev_port的指針成員變量rxqs也是一個表結構,它聯系着多個隊列內容。隊列的結構是struct dp_netdev_rxq.它的成員變量core_id表示該隊列所在的core。
4.3 通過struct dp_netdev_rxq的成員指針變量rxq,聯系到dp層的struct netdev_rxq結構內容。structdp_netdev_rxq的成員變量queue_id表示該隊列id。
5) netdev層
5.1 由於struct netdev結構內容是struct netdev_dpdk結構的一個up成員變量。所以通過struct netdev地址可以cast到struct netdev_dpdk結構內容。
5.2 通過struct detdev_dpdk結構內容的成員指針變量netdev_class和dpdk類型網卡交互。
5.3 由於struct netdev_rxq結構內容是struct netdev_rxq_dpdk結構內容的一個up成員變量。所以通過struct netdev_rxq地址可以cast到struct netdev_rxq_dpdk結構內容。該內容是dpdk類型的netdev_rxq,它的成員變量port_id是dpdk網絡設備的port。
5. vswitchd啟動
簡要的概括下vswitchd啟動的內容。
1) 和ovsdb-server通信初始化,建立
2) dpdk初始化
3) ofproto(OpenFlow交換機)初始化
4) netdev初始化
5) 網橋配置
更新網橋(添加、刪除)
更新端口
創建ofproto(OpenFlow交換機),創建或打開dpif_backer
添加端口,添加到pmd中
配置網橋、端口屬性
datapath run
dp_netdev的pmd更新
dpif_run(運行dpif-netdev的datapath,配置netdev等,接收發送報文)
ofproto run,配置ofproto需要的參數和功能(netflow、sflow、ipfix、mac學習)
OpenFlow控制器連接相關run
6. 附錄-數據結構查詢
6.1. ofproto_class
是一個OpenFlow交換機的實現類結構,提供的類功能很多,主要分為:
1. 工廠函數功能。如初始化、枚舉支持的datapath類型。
2. datapath類型處理。
3. OpenFlow交換機功能。如構造、運行等。
4. OpenFlow流表功能。
5. OpenFlow配置。
6. OpenFlow計量。
7. OpenFlow 1.1 group功能。
8. 獲取datapath信息功能。
9. 連接表功能。
6.2. dpif_class
datapath接口實現類,每種datapath都有對應的接口實現實例。dpif有兩種實現類:dpif_netlink_class、dpif_netdev_class。即system、netdev。比如,dpdk的netdevdatapath類型的實現實例dpif_netdev_class。
6.3. netdev_class
網絡設備實現類。包括抽象的設備結構和收發包功能。
6.4. bridge
6.5. ofproto
6.6. port
6.7. iface
6.8. ofport
6.9. ofport_dpif
6.10. ofproto_port
6.11. dpif_port
dp向dpif返回的端口信息。
6.12. ofproto_dpif
6.13. dpif_backer
datapath類型共享實現實例。
6.14. udpif
6.15. dpif
datapath接口。
6.16. dpif_netdev
netdev類型的datapath的接口。
6.17. dp_netdev
基於網絡設備接口的datapath。
6.18. dp_netdev_port
基於netdev類型的datapath的端口。
掛載在dp_netdev的ports成員中。
6.19. netdev
抽象的網絡設備。
6.20. netdev_dpdk
dpdk類型的netdev結構,繼承自netdev結構。
6.21. dp_netdev_rxq
用來表示core和隊列的對應關系。
6.22. netdev_rxq
網絡設備的收包隊列結構,以隊列區分。
6.23. netdev_rxq_dpdk
dpdk的收包隊列結構,繼承自netdev_rxq.
端口號和netdev_dpdk中的端口號相同。
使用dpdk的接口函數rte_zmalloc申請,受dpdk的內存管理控制。
6.24. dp_netdev_pmd_thread
6.25. rxq_poll
6.26. tx_port
OVS常用命令與使用總結
說明
在平時使用ovs中,經常用到的ovs命令,參數,與舉例總結,持續更新中…
進程啟動
1.先准備ovs的工作目錄,數據庫存儲路徑等
mkdir -p /etc/openvswitch mkdir -p /var/run/openvswitch
2.先啟動ovsdb-server
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
ps: 如果想清除配置,可以先刪除/etc/openvswitch/*,然后再手動創建一個數據庫:
ovsdb-tool create /etc/openvswitch/conf.db /usr/share/openvswitch/vswitch.ovsschema
3.初始化ovsdb
針對於新創建的數據庫才需要初始化
ovs-vsctl --no-wait init
4.啟動vswitchd進程
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
ovs常用命令
控制管理類
1.查看網橋和端口
ovs-vsctl show
- 1
2.創建一個網橋
ovs-vsctl add-br br0 ovs-vsctl set bridge br0 datapath_type=netdev
3.添加/刪除一個端口
# for system interfaces ovs-vsctl add-port br0 eth1 ovs-vsctl del-port br0 eth1 # for DPDK ovs-vsctl add-port br0 dpdk1 -- set interface dpdk1 type=dpdk options:dpdk-devargs=0000:01:00.0 # for DPDK bonds ovs-vsctl add-bond br0 dpdkbond0 dpdk1 dpdk2 \ -- set interface dpdk1 type=dpdk options:dpdk-devargs=0000:01:00.0 \ -- set interface dpdk2 type=dpdk options:dpdk-devargs=0000:02:00.0
4.設置/清除網橋的openflow協議版本
ovs-vsctl set bridge br0 protocols=OpenFlow13 ovs-vsctl clear bridge br0 protocols
5.查看某網橋當前流表
ovs-ofctl dump-flows br0 ovs-ofctl -O OpenFlow13 dump-flows br0 ovs-appctl bridge/dump-flows br0
6.設置/刪除控制器
ovs-vsctl set-controller br0 tcp:1.2.3.4:6633 ovs-vsctl del-controller br0
7.查看控制器列表
ovs-vsctl list controller
8.設置/刪除被動連接控制器
ovs-vsctl set-manager tcp:1.2.3.4:6640 ovs-vsctl get-manager ovs-vsctl del-manager
9.設置/移除可選選項
ovs-vsctl set Interface eth0 options:link_speed=1G ovs-vsctl remove Interface eth0 options link_speed
10.設置fail模式,支持standalone或者secure
standalone(default):清除所有控制器下發的流表,ovs自己接管
secure:按照原來流表繼續轉發
ovs-vsctl del-fail-mode br0 ovs-vsctl set-fail-mode br0 secure ovs-vsctl get-fail-mode br0
11.查看接口id等
ovs-appctl dpif/show
12.查看接口統計
ovs-ofctl dump-ports br0
流表類
流表操作
1.添加普通流表
ovs-ofctl add-flow br0 in_port=1,actions=output:2
2.刪除所有流表
ovs-ofctl del-flows br0
3.按匹配項來刪除流表
ovs-ofctl del-flows br0 "in_port=1"
匹配項
1.匹配vlan tag,范圍為0-4095
ovs-ofctl add-flow br0 priority=401,in_port=1,dl_vlan=777,actions=output:2
2.匹配vlan pcp,范圍為0-7
ovs-ofctl add-flow br0 priority=401,in_port=1,dl_vlan_pcp=7,actions=output:2
3.匹配源/目的MAC
ovs-ofctl add-flow br0 in_port=1,dl_src=00:00:00:00:00:01/00:00:00:00:00:01,actions=output:2 ovs-ofctl add-flow br0 in_port=1,dl_dst=00:00:00:00:00:01/00:00:00:00:00:01,actions=output:2
4.匹配以太網類型,范圍為0-65535
ovs-ofctl add-flow br0 in_port=1,dl_type=0x0806,actions=output:2
5.匹配源/目的IP
條件:指定dl_type=0x0800,或者ip/tcp
ovs-ofctl add-flow br0 ip,in_port=1,nw_src=10.10.0.0/16,actions=output:2 ovs-ofctl add-flow br0 ip,in_port=1,nw_dst=10.20.0.0/16,actions=output:2
6.匹配協議號,范圍為0-255
條件:指定dl_type=0x0800或者ip
# ICMP ovs-ofctl add-flow br0 ip,in_port=1,nw_proto=1,actions=output:2
7.匹配IP ToS/DSCP,tos范圍為0-255,DSCP范圍為0-63
條件:指定dl_type=0x0800/0x86dd,並且ToS低2位會被忽略(DSCP值為ToS的高6位,並且低2位為預留位)
ovs-ofctl add-flow br0 ip,in_port=1,nw_tos=68,actions=output:2 ovs-ofctl add-flow br0 ip,in_port=1,ip_dscp=62,actions=output:2
8.匹配IP ecn位,范圍為0-3
條件:指定dl_type=0x0800/0x86dd
ovs-ofctl add-flow br0 ip,in_port=1,ip_ecn=2,actions=output:2
9.匹配IP TTL,范圍為0-255
ovs-ofctl add-flow br0 ip,in_port=1,nw_ttl=128,actions=output:2
10.匹配tcp/udp,源/目的端口,范圍為0-65535
# 匹配源tcp端口179 ovs-ofctl add-flow br0 tcp,tcp_src=179/0xfff0,actions=output:2 # 匹配目的tcp端口179 ovs-ofctl add-flow br0 tcp,tcp_dst=179/0xfff0,actions=output:2 # 匹配源udp端口1234 ovs-ofctl add-flow br0 udp,udp_src=1234/0xfff0,actions=output:2 # 匹配目的udp端口1234 ovs-ofctl add-flow br0 udp,udp_dst=1234/0xfff0,actions=output:2
11.匹配tcp flags
tcp flags=fin,syn,rst,psh,ack,urg,ece,cwr,ns
ovs-ofctl add-flow br0 tcp,tcp_flags=ack,actions=output:2
12.匹配icmp code,范圍為0-255
條件:指定icmp
ovs-ofctl add-flow br0 icmp,icmp_code=2,actions=output:2
13.匹配vlan TCI
TCI低12位為vlan id,高3位為priority,例如tci=0xf123則vlan_id為0x123和vlan_pcp=7
ovs-ofctl add-flow br0 in_port=1,vlan_tci=0xf123,actions=output:2
14.匹配mpls label
條件:指定dl_type=0x8847/0x8848
ovs-ofctl add-flow br0 mpls,in_port=1,mpls_label=7,actions=output:2
15.匹配mpls tc,范圍為0-7
條件:指定dl_type=0x8847/0x8848
ovs-ofctl add-flow br0 mpls,in_port=1,mpls_tc=7,actions=output:2
16.匹配tunnel id,源/目的IP
# 匹配tunnel id ovs-ofctl add-flow br0 in_port=1,tun_id=0x7/0xf,actions=output:2 # 匹配tunnel源IP ovs-ofctl add-flow br0 in_port=1,tun_src=192.168.1.0/255.255.255.0,actions=output:2 # 匹配tunnel目的IP ovs-ofctl add-flow br0 in_port=1,tun_dst=192.168.1.0/255.255.255.0,actions=output:2
一些匹配項的速記符
速記符 | 匹配項 |
---|---|
ip | dl_type=0x800 |
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 |
指令動作
1.動作為出接口
從指定接口轉發出去
ovs-ofctl add-flow br0 in_port=1,actions=output:2
2.動作為指定group
group id為已創建的group table
ovs-ofctl add-flow br0 in_port=1,actions=group:666
3.動作為normal
轉為L2/L3處理流程
ovs-ofctl add-flow br0 in_port=1,actions=normal
4.動作為flood
從所有物理接口轉發出去,除了入接口和已關閉flooding的接口
ovs-ofctl add-flow br0 in_port=1,actions=flood
5.動作為all
從所有物理接口轉發出去,除了入接口
ovs-ofctl add-flow br0 in_port=1,actions=all
6.動作為local
一般是轉發給本地網橋
ovs-ofctl add-flow br0 in_port=1,actions=local
7.動作為in_port
從入接口轉發回去
ovs-ofctl add-flow br0 in_port=1,actions=in_port
8.動作為controller
以packet-in消息上送給控制器
ovs-ofctl add-flow br0 in_port=1,actions=controller
9.動作為drop
丟棄數據包操作
ovs-ofctl add-flow br0 in_port=1,actions=drop
10.動作為mod_vlan_vid
修改報文的vlan id,該選項會使vlan_pcp置為0
ovs-ofctl add-flow br0 in_port=1,actions=mod_vlan_vid:8,output:2
11.動作為mod_vlan_pcp
修改報文的vlan優先級,該選項會使vlan_id置為0
ovs-ofctl add-flow br0 in_port=1,actions=mod_vlan_pcp:7,output:2
12.動作為strip_vlan
剝掉報文內外層vlan tag
ovs-ofctl add-flow br0 in_port=1,actions=strip_vlan,output:2
13.動作為push_vlan
在報文外層壓入一層vlan tag,需要使用openflow1.1以上版本兼容
ovs-ofctl add-flow -O OpenFlow13 br0 in_port=1,actions=push_vlan:0x8100,set_field:4097-\>vlan_vid,output:2
ps: set field值為4096+vlan_id,並且vlan優先級為0,即4096-8191,對應的vlan_id為0-4095
14.動作為push_mpls
修改報文的ethertype,並且壓入一個MPLS LSE
ovs-ofctl add-flow br0 in_port=1,actions=push_mpls:0x8847,set_field:10-\>mpls_label,output:2
15.動作為pop_mpls
剝掉最外層mpls標簽,並且修改ethertype為非mpls類型
ovs-ofctl add-flow br0 mpls,in_port=1,mpls_label=20,actions=pop_mpls:0x0800,output:2
16.動作為修改源/目的MAC,修改源/目的IP
# 修改源MAC ovs-ofctl add-flow br0 in_port=1,actions=mod_dl_src:00:00:00:00:00:01,output:2 # 修改目的MAC ovs-ofctl add-flow br0 in_port=1,actions=mod_dl_dst:00:00:00:00:00:01,output:2 # 修改源IP ovs-ofctl add-flow br0 in_port=1,actions=mod_nw_src:192.168.1.1,output:2 # 修改目的IP ovs-ofctl add-flow br0 in_port=1,actions=mod_nw_dst:192.168.1.1,output:2
17.動作為修改TCP/UDP/SCTP源目的端口
# 修改TCP源端口 ovs-ofctl add-flow br0 tcp,in_port=1,actions=mod_tp_src:67,output:2 # 修改TCP目的端口 ovs-ofctl add-flow br0 tcp,in_port=1,actions=mod_tp_dst:68,output:2 # 修改UDP源端口 ovs-ofctl add-flow br0 udp,in_port=1,actions=mod_tp_src:67,output:2 # 修改UDP目的端口 ovs-ofctl add-flow br0 udp,in_port=1,actions=mod_tp_dst:68,output:2
18.動作為mod_nw_tos
條件:指定dl_type=0x0800
修改ToS字段的高6位,范圍為0-255,值必須為4的倍數,並且不會去修改ToS低2位ecn值
ovs-ofctl add-flow br0 ip,in_port=1,actions=mod_nw_tos:68,output:2
19.動作為mod_nw_ecn
條件:指定dl_type=0x0800,需要使用openflow1.1以上版本兼容
修改ToS字段的低2位,范圍為0-3,並且不會去修改ToS高6位的DSCP值
ovs-ofctl add-flow br0 ip,in_port=1,actions=mod_nw_ecn:2,output:2
20.動作為mod_nw_ttl
修改IP報文ttl值,需要使用openflow1.1以上版本兼容
ovs-ofctl add-flow -O OpenFlow13 br0 in_port=1,actions=mod_nw_ttl:6,output:2
21.動作為dec_ttl
對IP報文進行ttl自減操作
ovs-ofctl add-flow br0 in_port=1,actions=dec_ttl,output:2
22.動作為set_mpls_label
對報文最外層mpls標簽進行修改,范圍為20bit值
ovs-ofctl add-flow br0 in_port=1,actions=set_mpls_label:666,output:2
23.動作為set_mpls_tc
對報文最外層mpls tc進行修改,范圍為0-7
ovs-ofctl add-flow br0 in_port=1,actions=set_mpls_tc:7,output:2
24.動作為set_mpls_ttl
對報文最外層mpls ttl進行修改,范圍為0-255
ovs-ofctl add-flow br0 in_port=1,actions=set_mpls_ttl:255,output:2
25.動作為dec_mpls_ttl
對報文最外層mpls ttl進行自減操作
ovs-ofctl add-flow br0 in_port=1,actions=dec_mpls_ttl,output:2
26.動作為move NXM字段
使用move參數對NXM字段進行操作
# 將報文源MAC復制到目的MAC字段,並且將源MAC改為00:00:00:00:00:01 ovs-ofctl add-flow br0 in_port=1,actions=move:NXM_OF_ETH_SRC[]-\>NXM_OF_ETH_DST[],mod_dl_src:00:00:00:00:00:01,output:2
ps: 常用NXM字段參照表
NXM字段 | 報文字段 |
---|---|
NXM_OF_ETH_SRC | 源MAC |
NXM_OF_ETH_DST | 目的MAC |
NXM_OF_ETH_TYPE | 以太網類型 |
NXM_OF_VLAN_TCI | vid |
NXM_OF_IP_PROTO | IP協議號 |
NXM_OF_IP_TOS | IP ToS值 |
NXM_NX_IP_ECN | IP ToS ECN |
NXM_OF_IP_SRC | 源IP |
NXM_OF_IP_DST | 目的IP |
NXM_OF_TCP_SRC | TCP源端口 |
NXM_OF_TCP_DST | TCP目的端口 |
NXM_OF_UDP_SRC | UDP源端口 |
NXM_OF_UDP_DST | UDP目的端口 |
NXM_OF_SCTP_SRC | SCTP源端口 |
NXM_OF_SCTP_DST | SCTP目的端口 |
27.動作為load NXM字段
使用load參數對NXM字段進行賦值操作
# push mpls label,並且把10(0xa)賦值給mpls label ovs-ofctl add-flow br0 in_port=1,actions=push_mpls:0x8847,load:0xa-\>OXM_OF_MPLS_LABEL[],output:2 # 對目的MAC進行賦值 ovs-ofctl add-flow br0 in_port=1,actions=load:0x001122334455-\>OXM_OF_ETH_DST[],output:2
28.動作為pop_vlan
彈出報文最外層vlan tag
ovs-ofctl add-flow br0 in_port=1,dl_type=0x8100,dl_vlan=777,actions=pop_vlan,output:2
meter表
常用操作
由於meter表是openflow1.3版本以后才支持,所以所有命令需要指定OpenFlow1.3版本以上
ps: 在openvswitch-v2.8之前的版本中,還不支持meter
在v2.8版本之后已經實現,要正常使用的話,需要注意的是datapath類型要指定為netdev,band type暫時只支持drop,還不支持DSCP REMARK
1.查看當前設備對meter的支持
ovs-ofctl -O OpenFlow13 meter-features br0
2.查看meter表
ovs-ofctl -O OpenFlow13 dump-meters br0
3.查看meter統計
ovs-ofctl -O OpenFlow13 meter-stats br0
4.創建meter表
# 限速類型以kbps(kilobits per second)計算,超過20kb/s則丟棄 ovs-ofctl -O OpenFlow13 add-meter br0 meter=1,kbps,band=type=drop,rate=20 # 同上,增加burst size參數 ovs-ofctl -O OpenFlow13 add-meter br0 meter=2,kbps,band=type=drop,rate=20,burst_size=256 # 同上,增加stats參數,對meter進行計數統計 ovs-ofctl -O OpenFlow13 add-meter br0 meter=3,kbps,stats,band=type=drop,rate=20,burst_size=256 # 限速類型以pktps(packets per second)計算,超過1000pkt/s則丟棄 ovs-ofctl -O OpenFlow13 add-meter br0 meter=4,pktps,band=type=drop,rate=1000
5.刪除meter表
# 刪除全部meter表 ovs-ofctl -O OpenFlow13 del-meters br0 # 刪除meter id=1 ovs-ofctl -O OpenFlow13 del-meter br0 meter=1
6.創建流表
ovs-ofctl -O OpenFlow13 add-flow br0 in_port=1,actions=meter:1,output:2
group表
由於group表是openflow1.1版本以后才支持,所以所有命令需要指定OpenFlow1.1版本以上
常用操作
group table支持4種類型
- all:所有buckets都執行一遍
- select: 每次選擇其中一個bucket執行,常用於負載均衡應用
- ff(FAST FAILOVER):快速故障修復,用於檢測解決接口等故障
- indirect:間接執行,類似於一個函數方法,被另一個group來調用
1.查看當前設備對group的支持
ovs-ofctl -O OpenFlow13 dump-group-features br0
2.查看group表
ovs-ofctl -O OpenFlow13 dump-groups br0
3.創建group表
# 類型為all
ovs-ofctl -O OpenFlow13 add-group br0 group_id=1,type=all,bucket=output:1,bucket=output:2,bucket=output:3
# 類型為select ovs-ofctl -O OpenFlow13 add-group br0 group_id=2,type=select,bucket=output:1,bucket=output:2,bucket=output:3 # 類型為select,指定hash方法(5元組,OpenFlow1.5+) ovs-ofctl -O OpenFlow15 add-group br0 group_id=3,type=select,selection_method=hash,fields=ip_src,bucket=output:2,bucket=output:3
4.刪除group表
ovs-ofctl -O OpenFlow13 del-groups br0 group_id=2
5.創建流表
ovs-ofctl -O OpenFlow13 add-flow br0 in_port=1,actions=group:2
goto table配置
數據流先從table0開始匹配,如actions有goto_table,再進行后續table的匹配,實現多級流水線,如需使用goto table,則創建流表時,指定table id,范圍為0-255,不指定則默認為table0
1.在table0中添加一條流表條目
ovs-ofctl add-flow br0 table=0,in_port=1,actions=goto_table=1
2.在table1中添加一條流表條目
ovs-ofctl add-flow br0 table=1,ip,nw_dst=10.10.0.0/16,actions=output:2
tunnel配置
如需配置tunnel,必需確保當前系統對各tunnel的remote ip網絡可達
gre
1.創建一個gre接口,並且指定端口id=1001
ovs-vsctl add-port br0 gre1 -- set Interface gre1 type=gre options:remote_ip=1.1.1.1 ofport_request=1001
2.可選選項
將tos或者ttl在隧道上繼承,並將tunnel id設置成123
ovs-vsctl set Interface gre1 options:tos=inherit options:ttl=inherit options:key=123
3.創建關於gre流表
# 封裝gre轉發 ovs-ofctl add-flow br0 ip,in_port=1,nw_dst=10.10.0.0/16,actions=output:1001 # 解封gre轉發 ovs-ofctl add-flow br0 in_port=1001,actions=output:1
vxlan
1.創建一個vxlan接口,並且指定端口id=2001
ovs-vsctl add-port br0 vxlan1 -- set Interface vxlan1 type=vxlan options:remote_ip=1.1.1.1 ofport_request=2001
2.可選選項
將tos或者ttl在隧道上繼承,將vni設置成123,UDP目的端為設置成8472(默認為4789)
ovs-vsctl set Interface vxlan1 options:tos=inherit options:ttl=inherit options:key=123 options:dst_port=8472
3.創建關於vxlan流表
# 封裝vxlan轉發 ovs-ofctl add-flow br0 ip,in_port=1,nw_dst=10.10.0.0/16,actions=output:2001 # 解封vxlan轉發 ovs-ofctl add-flow br0 in_port=2001,actions=output:1
sflow配置
1.對網橋br0進行sflow監控
- agent: 與collector通信所在的網口名,通常為管理口
- target: collector監聽的IP地址和端口,端口默認為6343
- header: sFlow在采樣時截取報文頭的長度
- polling: 采樣時間間隔,單位為秒
ovs-vsctl -- --id=@sflow create sflow agent=eth0 target=\"10.0.0.1:6343\" header=128 sampling=64 polling=10 -- set bridge br0 sflow=@sflow
2.查看創建的sflow
ovs-vsctl list sflow
3.刪除對應的網橋sflow配置,參數為sFlow UUID
ovs-vsctl remove bridge br0 sflow 7b9b962e-fe09-407c-b224-5d37d9c1f2b3
4.刪除網橋下所有sflow配置
ovs-vsctl -- clear bridge br0 sflow
QoS配置
ingress policing
1.配置ingress policing,對接口eth0入流限速10Mbps
ovs-vsctl set interface eth0 ingress_policing_rate=10000 ovs-vsctl set interface eth0 ingress_policing_burst=8000
2.清除相應接口的ingress policer配置
ovs-vsctl set interface eth0 ingress_policing_rate=0 ovs-vsctl set interface eth0 ingress_policing_burst=0
3.查看接口ingress policer配置
ovs-vsctl list interface eth0
4.查看網橋支持的Qos類型
ovs-appctl qos/show-types br0
端口鏡像配置
1.配置eth0收到/發送的數據包鏡像到eth1
ovs-vsctl -- set bridge br0 mirrors=@m \ -- --id=@eth0 get port eth0 \ -- --id=@eth1 get port eth1 \ -- --id=@m create mirror name=mymirror select-dst-port=@eth0 select-src-port=@eth0 output-port=@eth1
2.刪除端口鏡像配置
ovs-vsctl -- --id=@m get mirror mymirror -- remove bridge br0 mirrors @m
3.清除網橋下所有端口鏡像配置
ovs-vsctl clear bridge br0 mirrors
4.查看端口鏡像配置
ovs-vsctl get bridge br0 mirrors
https://www.sdnlab.com/20966.html
2.設計考慮
2.1.整體數據結構
ovs datapath classifier涉及的數據結構主要有如下。
網橋數據結構
1
2
3
4
5
6
7
8
9
10
|
struct datapath {
struct rcu_head rcu;
/* 該結構使用雙鏈表組織 */
struct list_head list_node;
/* 從屬該datapath的流表項 */
struct flow_table table;
/* 從屬該datapath的vport信息 */
struct hlist_head *ports;
…
}
|
流表數據結構
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
struct flow_table {
/* 用key組織的流表實例 */
struct table_instance *ti;
/* 用unique flow identifier(ufid)組織的流表實例 */
struct table_instance *ufid_ti;
/* 掩碼信息緩存表 */
struct mask_cache_entry *mask_cache;
/* 掩碼信息列表 */
struct mask_array *mask_array;
/* 記錄當前節拍數 */
unsigned long last_rehash;
/* ti流表實例中存儲的流表項數目 */
unsigned int count;
/* ufid_ti流表實例中存儲的流表項數目 */
unsigned int ufid_count;
}
|
流表實例數據結構
1
2
3
4
5
6
7
8
9
10
|
struct table_instance {
/* 哈希桶,用於組織各個流表項的具體信息. */
struct flex_array *buckets;
/* 哈希桶大小 */
unsigned int n_buckets;
struct rcu_head rcu;
int node_ver;
u32 hash_seed;
bool keep_flows;
}
|
掩碼信息列表數據結構
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
struct mask_array {
struct rcu_head rcu;
/* 當前已存儲的掩碼信息數目 */
int count;
/* 一共可以存儲掩碼信息的總數目 */
int max;
/* 具體的掩碼信息 */
struct sw_flow_mask *masks[];
}
struct sw_flow_mask {
/* 引用計數 */
int ref_count;
struct rcu_head rcu;
/* 匹配關鍵字的有效范圍,下文會具體解釋 */
struct sw_flow_key_range range;
/* 匹配關鍵字信息 */
struct sw_flow_key key;
}
|
掩碼信息緩存表數據結構
1
2
3
4
5
6
|
struct mask_cache_entry {
/* hash值 */
u32 skb_hash;
/* 對應哪一個掩碼信息,索引值 */
u32 mask_index;
}
|
哈希桶數據結構
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
struct flex_array {
union {
struct {
/* 實際等於sizeof(struct hlist_head) */
int element_size;
/* 一共有多少個struct hlist_head指針. */
int total_nr_elements;
/* 每個part(頁)可以存儲多少個struct hlist_head指針 */
int elems_per_part;
u32 reciprocal_value reciprocal_elems;
/* 具體存儲struct hlist_head指針的緩沖區 */
struct flex_array_part *parts[];
};
/* 填充字段.代表了整個結構的大小=頁大小(4096字節) */
char padding[FLEX_ARRAY_BASE_SIZE];
}
}
struct flex_array_part {
/* 緩沖區.一個頁大小,即:4096字節 */
char elements[FLEX_ARRAY_PART_SIZE];
};
|
上述這些數據結構是在源碼中的ovs_flow_tbl_init函數里面進行初始化操作的。初始化后以及上述這些數據結構之間的關系如下圖所示。
2.2.關鍵信息范圍
從上面2.1節可知,掩碼信息(struct sw_flow_mask結構)中記錄了一個范圍(struct sw_flow_key_range結構),該范圍用於標識需要匹配的關鍵信息最小偏移和最大偏移。為什么需要這樣做?個人感覺和ovs-dpdk datapath classifier中描述的miniflow類似,即:匹配過程中並非使用報文的所有字段去匹配,而是使用部分字段,例如使用報文的五元組信息(源IP、目的IP、協議號、源端口、目的端口)。那么使用sw_flow_key_range結構來標識這五元組信息中最小偏移和最大偏移。實際源碼中,關鍵信息是使用struct sw_flow_key結構來描述的。由於該結構字段較多,這里不詳細給出。以報文五元組信息為例,這里給出的五元組信息所在sw_flow_key結構的位置和實際源碼對應的位置是不相同的,這里只是給出計算最小偏移和最大偏移的概念,如下圖所示,關鍵信息的有效范圍為:
最小偏移=M,最大偏移=N
2.3.更新過程
在源碼中,對應更新過程的入口函數是:ovs_flow_cmd_new。這個入口函數是處於內核模塊中,在接收到報文時,通過下面2.4節所述的查找過程,查找失敗時,會將報文的相關信息upcall到用戶空間,在用戶空間通過查找”慢路徑”將對應的actions和mask信息下發到內核空間,在內核空間,處理的入口函數正是ovs_flow_cmd_new。下面將對這個函數的處理過程作詳細描述。描述之前,先掌握一些相關的數據結構,如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
struct sw_flow {
struct rcu_head rcu;
/* 哈希桶鏈表 */
struct {
struct hlist_node node[2];
u32 hash;
} flow_table, ufid_table;
int stats_last_writer;
/* 關鍵信息.經過掩碼運算后的 */
struct sw_flow_key key;
/* 流id信息 */
struct sw_flow_id id;
/* 指向的掩碼信息 */
struct sw_flow_mask *mask;
/* 指向的流動作信息 */
struct sw_flow_actions *sf_acts;
struct flow_stats *stats[];
}
struct sw_flow_id {
u32 ufid_len;
union {
/* ufid為128比特,共16字節 */
u32 ufid[4];
struct sw_flow_key *unmasked_key;
}
}
struct sw_flow_actions {
struct rcu_head rcu;
size_t orig_len;
u32 actions_len;
struct nlattr actions[];
}
struct sw_flow_match {
struct sw_flow_key *key;
struct sw_flow_key_range range;
struct sw_flow_mask *mask;
}
|
具體流程如下所示:
1)初始化struct sw_flow *new_flow。分配空間給new_flow,並設置部分的成員值,如下:
new_flow->sf_acts = null;
new_flow->mask = null;
new_flow->id.ufid_len=0;
new_flow->id.unmasked_key=null;
2)構建struct sw_flow_match match信息。根據接收到的關鍵信息和掩碼信息,存儲在match.key和match.mask中。
3)計算new_flow->key = match.key & match.mask。
4)將ufid信息提取出來存儲在new_flow->id.ufid_len和new_flow->id.ufid中。如果未提供任何的UFID信息(此時的new_flow->id.ufid_len為0),則將match.key信息拷貝一份到new_flow->id.unmasked_key中存儲。
5)將actions信息存儲在變量struct sw_flow_actions acts中。
6)根據接收到的dp_ifindex獲取struct datapath dp信息。
7)如果new_flow->id.ufid_len不為0,則使用new_flow->id.ufid信息去dp->table.ufid_ti指向的哈希桶中找到對應的struct sw_flow信息。假設找不到。
8)而如果new_flow->id.ufid_len為0,則需要在dp->table.ti,同時配合在2)中構建出來的match.key和dp->table.mask_array配合找出對應的struct sw_flow信息。這個詳細過程可以參考下面的2.4節。這里也假設找不到。
9)經過上面7)和8)步都沒找到對應的struct sw_flow信息。首先設置new_flow->sf_acts=acts(在5)步中獲取)。然后使用match.mask去dp->table.mask_array中查找是否已存在該match.mask信息。如果不存在則接着分配一個struct sw_flow_mask *mask。設置mask->key=match.mask->key, mask->range=match.mask->range, mask->ref_count=1。並且在dp->table.mask_array.masks[i]中存儲該mask的地址。添加完畢之后,如下圖所示。變化的地方用綠色標注出來了。同時new_flow->mask=mask。
10)根據new_flow->key和new_flow->mask.range計算出new_flow->flow_table.hash值。
11)在dp->table.ti->buckets指向的哈希桶中插入new_flow。如下圖所示(假設插入的位置為part[0]對應的第0個位置)。dp->table.count。
12)如果new_flow->id.ufid_len不為0,則使用new_flow->id.ufid信息計算出new_flow->ufid_table.hash。然后根據這個hash值在dp->table.ufid_ti->buckets中找到合適的哈希桶存儲對應的new_flow信息。這里假設找到的位置為parts[0]的第0個位置。如下圖所示。插入new_flow信息之后,dp->table.ufid_count。
到此為止,更新過程完畢。
2.4.查找過程
在源碼中,對應查找過程的入口函數是:ovs_vport_receive。具體過程如下:
1)從接收到的報文信息中提取出關鍵信息。存儲到struct sw_flow_key key。
2)從接收到的報文信息中獲取struct vport信息,再從vport信息獲取struct datapath信息,假設為dp。
3)如果接收到的報文中未帶有skb_hash值信息,則執行:
遍歷每一個掩碼,即:dp->table.mask_array->masks[i]。這里簡單記為mask。將1)提取出來的關鍵新key與這個掩碼mask做邏輯與運算,得出掩碼過的信息,記為masked_key。通過masked_key和mask.range信息計算出hash值在dp->table.ti->buckets中找到對應的哈希桶,並遍歷該桶上的所有struct sw_flow信息,記為flow。執行比較:如果flow->mask指向的是mask,flow->flow_table.hash值和計算出的hash值相等,且flow->key和masked_key信息在mask.range指定的范圍內完全相同,則認為成功匹配,返回flow信息。否則匹配失敗,返回NULL。
4)如果接收到的報文中帶有skb_hash值信息,則執行:
4)-1:根據舊的skb_hash值和key->recirc_id值重新計算出新的skb_hash值。
4)-2:將skb_hash值分成4段,每段1字節,記為hash。遍歷每一段的hash值,執行:獲取dp->table.mask_cache[hash]表項,記為entry,如果entry.skb_hash值和skb_hash值相等,則使用entry.mask_index指向的掩碼信息去dp->table.ti->buckets中找到對應的struct sw_flow信息(過程和上述第3)步相同)。如果找到對應的flow,則返回flow,過程中會更新entry.mask_index值來實際指向具體的掩碼信息索引。否則,匹配失敗,將entry.skb_hash值置0,返回NULL。而如果每一個entry.skb_hash值和skb_hash值不相等,則遍歷完每一段hash值之后,從中選擇最佳候選entry,最佳候選的條件是:其skb_hash值最小者,假設記為ce。最后使用ce.mask_index指向的掩碼信息去dp->table.ti->buckets中找到對應的struct sw_flow信息(過程和上述第3)步相同)。如果找到對應的flow,則返回flow,同時將ce->skb_hash值更新為skb_hash值。當然在查找的過程中(上述第3)步),也會更新ce->mask_index來指向實際的掩碼信息索引。
5) 如果在上述第3)和第4)步中都匹配失敗了,則需要將報文的信息upcall到用戶空間,由用戶空間負責查找對應的流動作信息,返回給內核。這個過程具體請參考上述2.3節。
到此為止,查找過程結束。
2.5.執行過程
在2.4節查找過程中,如果查找成功,則需要執行對應的流動作。入口函數是:ovs_execute_actions。
而如果查找失敗,upcall到用戶空間,找到對應的流動作之后,調用執行,到內核空間,入口函數對應的是:ovs_packet_cmd_execute。該函數最終會調用到ovs_execute_actions。
2.6.掩碼信息比較
在上述2.3和2.4節所述的更新過程和查找過程中都會遇見用新構造的掩碼信息與datapath結構的table.mask_array作比較,以檢測掩碼信息是否需要新增。而比較的方法如下:
假設新構造的掩碼信息為mask,與table.mask_array->masksi比較:
1)mask.range.start和exist_mask.range.start相等。
2)mask.range.end和exist_mask.range.end相等。
3)mask和exist_mask在range范圍內完全相同。
同時符合上面三個條件才認為兩個掩碼完全相同。
2.7.流信息比較
在上述2.4節所述的查找過程中,對接收到的報文進行匹配表項時,需要對流信息作比較。假設接收到的報文提取出來的關鍵信息為key,匹配的掩碼信息為mask,通過mask & key計算出掩碼后的報文關鍵信息,記為masked_key,通過masked_key和mask.range計算出hash值。根據這個hash值找到對應的哈希桶,遍歷這個哈希桶中存儲的每一個流信息,記為flow。現在需要比較flow信息進而找到匹配的流表項。比較如下:
1)flow->mask指向的是mask。
2)flow->flow_table.hash和計算出的hash值相等。
3)flow->key和masked_key在mask.range范圍內完全相同。
同時符合上面三個條件才認為找到匹配的流表項。
2.8.mask_cache表項
在ovs_dp_process_packet函數中查找匹配的流表項時,如果報文的skb中已經帶有skb_hash值,則將這個skb_hash值(32比特)分為4段,每段8比特,每段的哈希值暫記為hash,用這個hash值去datapath結構中的table.mask_cache緩存表中查找對應的掩碼信息索引。每個表項(struct mask_cache_entry結構)存儲了skb_hash和mask_index信息。初始化的時候,這個緩存表中所有表項都置0,因此,用報文的skb_hash值,分4段去查找,都無法找到合適的表項。這時需要從中選出最佳候選的表項,而最佳候選的表項為其skb_hash值最小。因此,初始化的時候,最終會選擇table.mask_cache[0]為最佳候選表項。
接着根據上述2.4節所述的查找過程,找到合適的流表項信息。如果匹配成功,則最佳候選表項table.mask_cache[0].mask_index記錄了掩碼信息索引(index),即:table.mask_array->masks[index]。同時table.mask_cache[0].skb_hash賦值為skb_hash。假設index為0,則對應如下圖所示。
后續如果收到的報文帶的skb_hash值與table.mask_cache[0].skb_hash值相等時,則首先使用table.mask_cache[0].mask_index索引的掩碼信息去找匹配的流表信息。當然,如果匹配成功,table.mask_cache[0].mask_index可能並非為0(之前存儲的值),有可能更新為其他值。而如果匹配失敗,則table.mask_cache[0].skb_hash置0。
2.9.mask_array擴充
當datapath結構中的table.mask_array->count >= table.mask_array->max時,則需要擴充mask_array空間。已當前table.mask_array->max * 2的大小進行擴充。擴充前后的效果如下圖所示。橙色線指向的掩碼信息在擴充之后,會釋放掉old的掩碼信息空間。擴充總是按照當前max數值的2倍大小進行擴充,例如:16 -> 32 -> 64 -> 128 -> …。從源碼中暫時未看到這個擴充的最大值。
2.10.table_instance擴充
table_instance擴充有兩個條件觸發:
1)當datapath結構中的table.count > table.ti->n_buckets時,觸發擴充。
2)datapath結構中的table.last_rehash記錄了上次執行擴充或初始化table時系統的jiffies值。如果超過10分鍾,則需要重新擴充,只是這次的擴充並非增大空間,而是以相同的大小重新分配空間。覺得這樣做的意義是重新生成table_instance結構中的hash_seed值,重新安排哈希桶的鏈表長度,分散存儲,減少匹配的比較次數。
我們主要以第一種條件為例,描述擴充的過程。這種擴充是在原有的哈希桶數目(n_buckets)基礎之上,以2倍的大小進行擴充。擴充前后,table_instance結構的變化如下圖所示。
根據上圖可知,擴充之后,table_instance中的hash_seed更新為新的隨機數了。這樣,在從舊的table_instance將哈希桶中對應的各個流信息拷貝到新的table_instance時,需要重新計算哈希桶的位置,重新安排了。這樣做的好處時:可以重新分散每個哈希桶中流信息鏈表的長度,減少在匹配時流的比較次數。如下圖所示,之前Flow_A和Flow_B都位於第0個哈希桶,擴充之后,Flow_A處於了第0個哈希桶,而Flow_B則處於第1025個哈希桶了。這樣在查找Flow_B的時候,比較的次數就減少了一次。在大規模查找的過程中,這種改變可以大大提高查找的效率。
2.11.示例
假設ovs用戶空間的”慢路徑”存儲的流表信息如下所示:
1
2
3
|
table=0, src_ip=11.11.11.0/24, dst_ip=192.0.0.0/8, actions=output:1
table=0, src_ip=2.2.2.0/24, dst_ip=2.0.0.0/8, actions=output:2
table=0, dst_ip=8.0.0.0/8, actions=drop
|
初始化的時候,ovs內核空間的”快路徑”沒有存儲任何的流路徑信息。如下圖所示:
收到第一個報文:src_ip=11.11.11.25, dst_ip=192.1.1.1
收到第一個報文,匹配結果Miss,upcall到用戶空間,查表將結果發送回內核空間的datapath。根據上述2.3節所述的更新過程,會新增相應的掩碼信息(mask_A)和流信息(flow_A)。如下圖所示。
收到第二個報文:src_ip=11.11.11.63, dst_ip=192.168.7.8
根據上述2.4節所述的查找過程,匹配成功。
收到第三個報文:src_ip=2.2.2.4, dst_ip=2.7.7.7
根據上述2.4節所述的查找過程,匹配失敗。因為掩碼信息和mask_A完全相同,因此無需新增掩碼信息,只需要將mask_A的ref_count引用計數加1即可。但是需要新增相應的流信息(flow_B),如下圖所示。
收到第四個報文:dst_ip=8.12.34.56
根據上述2.4節所述的查找過程,匹配失敗。需要新增相應的掩碼信息(mask_B)和流信息(flow_C)。如下圖所示
openstack的虛擬機網卡、網橋等(tap、qbr、qvb、qvo)mtu設置
vim /usr/lib/python2.7/site-packages/nova/network/linux_net.py
|









openstack底層技術-使用openvswitch
原文地址
www.isjian.com/openstack/openstack-base-use-openvswitch
openstack底層技術-使用openvswitch
Open vSwitch介紹
在過去,數據中心的服務器是直接連在硬件交換機上,后來VMware實現了服務器虛擬化技術,使虛擬服務器(VMs)能夠連接在虛擬交換機上,借助這個虛擬交換機,可以為服務器上運行的VMs或容器提供邏輯的虛擬的以太網接口,這些邏輯接口都連接到虛擬交換機上,有三種比較流行的虛擬交換機: VMware virtual switch, Cisco Nexus 1000V,和Open vSwitch
Open vSwitch(OVS)是運行在虛擬化平台上的虛擬交換機,其支持OpenFlow協議,也支持gre/vxlan/IPsec等隧道技術。在OVS之前,基於Linux的虛擬化平台比如KVM或Xen上,缺少一個功能豐富的虛擬交換機,因此OVS迅速崛起並開始在Xen/KVM中流行起來,並且應用於越來越多的開源項目,比如openstack neutron中的網絡解決方案
在虛擬交換機的Flow控制器或管理工具方面,一些商業產品都集成有控制器或管理工具,比如Cisco 1000V的Virtual Supervisor Manager(VSM)
,VMware的分布式交換機中的vCenter
。而OVS則需要借助第三方控制器或管理工具實現復雜的轉發策略。例如OVS支持OpenFlow 協議,我們就可以使用任何支持OpenFlow協議的控制器來對OVS進行遠程管理。OpenStack Neutron中的ML2插件也能夠實現對OVS的管理。但這並不意味着OVS必須要有一個控制器才能工作。在不連接外部控制器情況下,OVS自身可以依靠MAC地址學習實現二層數據包轉發功能,就像Linux Bridge
在基於Linux內核的系統上,應用最廣泛的還是系統自帶的虛擬交換機Linux Bridge
,它是一個單純的基於MAC地址學習的二層交換機,簡單高效,但同時缺乏一些高級特性,比如OpenFlow,VLAN tag,QOS,ACL,Flow等,而且在隧道協議支持上,Linux Bridge只支持vxlan,OVS支持gre/vxlan/IPsec等,這也決定了OVS更適用於實現SDN技術
OVS支持以下features
- 支持NetFlow, IPFIX, sFlow, SPAN/RSPAN等流量監控協議
- 精細的ACL和QoS策略
- 可以使用OpenFlow和OVSDB協議進行集中控制
- Port bonding,LACP,tunneling(vxlan/gre/Ipsec)
- 適用於Xen,KVM,VirtualBox等hypervisors
- 支持標准的802.1Q VLAN協議
- 基於VM interface的流量管理策略
- 支持組播功能
- flow-caching engine(datapath模塊)
文章使用環境
centos7 openvswitch 2.5 OpenFlow 1.4`
OVS架構
先看下OVS整體架構,用戶空間主要組件有數據庫服務ovsdb-server和守護進程ovs-vswitchd。kernel中是datapath內核模塊。最上面的Controller表示OpenFlow控制器,控制器與OVS是通過OpenFlow協議進行連接,控制器不一定位於OVS主機上,下面分別介紹圖中各組件
ovs-vswitchd
ovs-vswitchd
守護進程是OVS的核心部件,它和datapath
內核模塊一起實現OVS基於流的數據交換。作為核心組件,它使用openflow協議與上層OpenFlow控制器通信,使用OVSDB協議與ovsdb-server
通信,使用netlink
和datapath
內核模塊通信。ovs-vswitchd
在啟動時會讀取ovsdb-server
中配置信息,然后配置內核中的datapaths
和所有OVS switches,當ovsdb中的配置信息改變時(例如使用ovs-vsctl工具),ovs-vswitchd
也會自動更新其配置以保持與數據庫同步
# ps -ef |grep ovs-vs root 22176 22175 0 Jan17 ? 00:16:56 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
ovs-vswitchd
需要加載datapath
內核模塊才能正常運行。它會自動配置datapath
flows,因此我們不必再使用ovs-dpctl
去手動操作datapath
,但ovs-dpctl
仍可用於調試場合
在OVS中,ovs-vswitchd
從OpenFlow控制器獲取流表規則,然后把從datapath
中收到的數據包在流表中進行匹配,找到匹配的flows並把所需應用的actions返回給datapath
,同時作為處理的一部分,ovs-vswitchd
會在datapath
中設置一條datapath flows用於后續相同類型的數據包可以直接在內核中執行動作,此datapath flows相當於OpenFlow flows的緩存。對於datapath
來說,其並不知道用戶空間OpenFlow的存在,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 ...
ovsdb-server
ovsdb-server
是OVS輕量級的數據庫服務,用於整個OVS的配置信息,包括接口/交換內容/VLAN等,OVS主進程ovs-vswitchd
根據數據庫中的配置信息工作,下面是ovsdb-server
進程詳細信息
ps -ef |grep ovsdb-server root 22166 22165 0 Jan17 ? 00:02:32 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
或其它命令工具(ovsdb-client)通過此socket連接管理ovsdb /var/log/openvswitch/ovsdb-server.log
是日志記錄
OpenFlow
OpenFlow是開源的用於管理交換機流表的協議,OpenFlow在OVS中的地位可以參考上面架構圖,它是Controller和ovs-vswitched間的通信協議。需要注意的是,OpenFlow是一個獨立的完整的流表協議,不依賴於OVS,OVS只是支持OpenFlow協議,有了支持,我們可以使用OpenFlow控制器來管理OVS中的流表,OpenFlow不僅僅支持虛擬交換機,某些硬件交換機也支持OpenFlow協議
OVS常用作SDN交換機(OpenFlow交換機),其中控制數據轉發策略的就是OpenFlow flow。OpenStack Neutron中實現了一個OpenFlow控制器用於向OVS下發OpenFlow flows控制虛擬機間的訪問或隔離。本文討論的默認是作為SDN交換機場景下
OpenFlow flow的流表項存放於用戶空間主進程ovs-vswitchd
中,OVS除了連接OpenFlow控制器獲取這種flow,文章后面會提到的命令行工具ovs-ofctl
工具也可以手動管理OVS中的OpenFlow flow,可以查看man ovs-ofctl
了解
在OVS中,OpenFlow flow是最重要的一種flow, 然而還有其它幾種flows存在,文章下面OVS概念部分會提到
Controller
Controller指OpenFlow控制器。OpenFlow控制器可以通過OpenFlow協議連接到任何支持OpenFlow的交換機,比如OVS。控制器通過向交換機下發流表規則來控制數據流向。除了可以通過OpenFlow控制器配置OVS中flows,也可以使用OVS提供的ovs-ofctl
命令通過OpenFlow協議去連接OVS,從而配置flows,命令也能夠對OVS的運行狀況進行動態監控。
Kernel Datapath
下面討論場景是OVS作為一個OpenFlow交換機
datapath是一個Linux內核模塊,它負責執行數據交換。關於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.
為了說明datapath,來看一張更詳細的架構圖,圖中的大部分組件上面都有提到
用戶空間ovs-vswitchd
和內核模塊datapath
決定了數據包的轉發,首先,datapath
內核模塊收到進入數據包(物理網卡或虛擬網卡),然后查找其緩存(datapath flows),當有一個匹配的flow時它執行對應的操作,否則datapath
會把該數據包送入用戶空間由ovs-vswitchd
負責在其OpenFlow flows中查詢(圖1中的First Packet),ovs-vswitchd
查詢后把匹配的actions返回給datapath
並設置一條datapath flows到datapath
中,這樣后續進入的同類型的數據包(圖1中的Subsequent Packets)因為緩存匹配會被datapath
直接處理,不用再次進入用戶空間。
datapath
專注於數據交換,它不需要知道OpenFlow的存在。與OpenFlow打交道的是ovs-vswitchd
,ovs-vswitchd
存儲所有Flow規則供datapath
查詢或緩存.
雖然有ovs-dpctl
管理工具的存在,但我們沒必要去手動管理datapath
,這是用戶空間ovs-vswitchd
的工作
OVS概念
這部分說下OVS中的重要概念,使用OpenStack neutron+vxlan部署模式下網絡節點OVS網橋作為例子
# ovs-vsctl show e44abab7-2f65-4efd-ab52-36e92d9f0200 Manager "ptcp:6640:127.0.0.1" is_connected: true Bridge br-ext Controller "tcp:127.0.0.1:6633" is_connected: true fail_mode: secure Port br-ext Interface br-ext type: internal Port "eth1" Interface "eth1" Port phy-br-ext Interface phy-br-ext type: patch options: {peer=int-br-ext} Bridge br-tun Controller "tcp:127.0.0.1:6633" is_connected: true fail_mode: secure Port br-tun Interface br-tun type: internal Port patch-int Interface patch-int type: patch options: {peer=patch-tun} Port "vxlan-080058ca" Interface "vxlan-080058ca" type: vxlan options: {df_default="true", in_key=flow, local_ip="8.0.88.201", out_key=flow, remote_ip="8.0.88.202"} Bridge br-int Controller "tcp:127.0.0.1:6633" is_connected: true fail_mode: secure Port "qr-11591618-c4" tag: 3 Interface "qr-11591618-c4" type: internal Port patch-tun Interface patch-tun type: patch options: {peer=patch-int} Port int-br-ext Interface int-br-ext type: patch options: {peer=phy-br-ext}
Bridge
Bridge代表一個以太網交換機(Switch),一個主機中可以創建一個或者多個Bridge。Bridge的功能是根據一定規則,把從端口收到的數據包轉發到另一個或多個端口,上面例子中有三個Bridge,br-tun
,br-int
,br-ext
添加一個網橋br0
ovs-vsctl add-br br0
Port
端口Port與物理交換機的端口概念類似,Port是OVS Bridge上創建的一個虛擬端口,每個Port都隸屬於一個Bridge。Port有以下幾種類型
- Normal
可以把操作系統中已有的網卡(物理網卡em1/eth0,或虛擬機的虛擬網卡tapxxx)掛載到ovs上,ovs會生成一個同名Port處理這塊網卡進出的數據包。此時端口類型為Normal。
如下,主機中有一塊物理網卡eth1
,把其掛載到OVS網橋br-ext
上,OVS會自動創建同名Port eth1
。
ovs-vsctl add-port br-ext eth1
#Bridge br-ext中出現Port "eth1"
有一點要注意的是,掛載到OVS上的網卡設備不支持分配IP地址,因此若之前eth1
配置有IP地址,掛載到OVS之后IP地址將不可訪問。這里的網卡設備不只包括物理網卡,也包括主機上創建的虛擬網卡
- Internal
Internal類型是OVS內部創建的虛擬網卡接口,每創建一個Port,OVS會自動創建一個同名接口(Interface)掛載到新創建的Port上。接口的概念下面會提到。
下面創建一個網橋br0,並創建一個Internal類型的Port p0
ovs-vsctl add-br br0 ovs-vsctl add-port br0 p0 -- set Interface p0 type=internal #查看網橋br0 ovs-vsctl show br0 Bridge "br0" fail_mode: secure Port "p0" Interface "p0" type: internal Port "br0" Interface "br0" type: internal
可以看到有兩個Port。當ovs創建一個新網橋時,默認會創建一個與網橋同名的Internal Port。在OVS中,只有”internal”類型的設備才支持配置IP地址信息,因此我們可以為br0
接口配置一個IP地址,當然p0
也可以配置IP地址
ip addr add 192.168.10.11/24 dev br0 ip link set br0 up #添加默認路由 ip route add default via 192.168.10.1 dev br0
上面兩種Port類型區別在於,Internal類型會自動創建接口(Interface),而Normal類型是把主機中已有的網卡接口添加到OVS中
- Patch
當主機中有多個ovs網橋時,可以使用Patch Port把兩個網橋連起來。Patch Port總是成對出現,分別連接在兩個網橋上,從一個Patch Port收到的數據包會被轉發到另一個Patch Port,類似於Linux系統中的veth
。使用Patch連接的兩個網橋跟一個網橋沒什么區別,OpenStack Neutron中使用到了Patch Port。上面網橋br-ext
中的Port phy-br-ext
與br-int
中的Port int-br-ext
是一對Patch Port
可以使用ovs-vsctl
創建patch設備,如下創建兩個網橋br0,br1
,然后使用一對Patch Port
連接它們
ovs-vsctl add-br br0 ovs-vsctl add-br br1 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 #結果如下 #ovs-vsctl show Bridge "br0" Port "br0" Interface "br0" type: internal Port "patch0" Interface "patch0" type: patch options: {peer="patch1"} Bridge "br1" Port "br1" Interface "br1" type: internal Port "patch1" Interface "patch1" type: patch options: {peer="patch0"}
連接兩個網橋不止上面一種方法,linux中支持創建Veth
設備對,我們可以首先創建一對Veth
設備對,然后把這兩個Veth
分別添加到兩個網橋上,其效果跟OVS中創建Patch Port一樣,只是性能會有差別
#創建veth設備對veth-a,veth-b ip link add veth-a type veth peer name veth-b #使用Veth連接兩個網橋 ovs-vsctl add-port br0 veth-a ovs-vsctl add-port br1 veth-b
- Tunnel
OVS中支持添加隧道(Tunnel)端口,常見隧道技術有兩種gre
或vxlan
。隧道技術是在現有的物理網絡之上構建一層虛擬網絡,上層應用只與虛擬網絡相關,以此實現的虛擬網絡比物理網絡配置更加靈活,並能夠實現跨主機的L2通信以及必要的租戶隔離。不同隧道技術其大體思路均是將以太網報文使用隧道協議封裝,然后使用底層IP網絡轉發封裝后的數據包,其差異性在於選擇和構造隧道的協議不同。Tunnel在OpenStack中用作實現大二層網絡以及租戶隔離,以應對公有雲大規模,多租戶的復雜網絡環境。
OpenStack是多節點結構,同一子網的虛擬機可能被調度到不同計算節點上,因此需要有隧道技術來保證這些同子網不同節點上的虛擬機能夠二層互通,就像他們連接在同一個交換機上,同時也要保證能與其它子網隔離。
OVS在計算和網絡節點上建立隧道Port來連接各節點上的網橋br-int
,這樣所有網絡和計算節點上的br-int
互聯形成了一個大的虛擬的跨所有節點的邏輯網橋(內部靠tunnel id或VNI隔離不同子網),這個邏輯網橋對虛擬機和qrouter是透明的,它們覺得自己連接到了一個大的br-int
上。從某個計算節點虛擬機發出的數據包會被封裝進隧道通過底層網絡傳輸到目的主機然后解封裝。
上面網橋br-tun
中Port "vxlan-080058ca"
就是一個vxlan
類型tunnel端口。下面使用兩台主機測試創建vxlan隧道
#主機192.168.7.21上 ovs-vsctl add-br br-vxlan #主機192.168.7.23上 ovs-vsctl add-br br-vxlan #主機192.168.7.21上添加連接到7.23的Tunnel Port ovs-vsctl add-port br-vxlan tun0 -- set Interface tun0 type=vxlan options:remote_ip=192.168.7.23 #主機192.168.7.23上添加連接到7.21的Tunnel Port ovs-vsctl add-port br-vxlan tun0 -- set Interface tun0 type=vxlan options:remote_ip=192.168.7.21
然后,兩個主機上橋接到br-vxlan
的虛擬機就像連接到同一個交換機一樣,可以實現跨主機的L2連接,同時又完全與物理網絡隔離。
Interface
Interface是連接到Port的網絡接口設備,是OVS與外部交換數據包的組件,在通常情況下,Port和Interface是一對一的關系,只有在配置Port為 bond模式后,Port和Interface是一對多的關系。這個網絡接口設備可能是創建Internal
類型Port時OVS自動生成的虛擬網卡,也可能是系統的物理網卡或虛擬網卡(TUN/TAP)掛載在ovs上。 OVS中只有”Internal”類型的網卡接口才支持配置IP地址
Interface
是一塊網絡接口設備,負責接收或發送數據包,Port是OVS網橋上建立的一個虛擬端口,Interface
掛載在Port上。
Controller
OpenFlow控制器。OVS可以同時接受一個或者多個OpenFlow控制器的管理。主要作用是下發流表(Flow Tables)到OVS,控制OVS數據包轉發規則。控制器與OVS通過網絡連接,不一定要在同一主機上
可以看到上面實例中三個網橋br-int
,br-ext
,br-tun
都連接到控制器Controller "tcp:127.0.0.1:6633
上
datapath
OVS內核模塊,負責執行數據交換。其內部有作為緩存使用的flows,關於datapath,下面會細說
流(flows)
flows是OVS進行數據轉發策略控制的核心數據結構,區別於Linux Bridge是個單純基於MAC地址學習的二層交換機,flows的存在使OVS作為一款SDN交換機成為雲平台網絡虛擬機化主要組件
OVS中有多種flows存在,用於不同目的,但最主要的還是OpenFlow flows這種,文中未明確說明的flows都是指OpenFlow flows
- OpenFlow flows
OVS中最重要的一種flows,Controller控制器下發的就是這種flows,OVS架構部分已經簡單介紹過,關於openflow的具體使用,會在另一篇文章中說明
- “hidden” flows
OVS在使用OpenFlow flow時,需要與OpenFlow控制器建立TCP連接,若此TCP連接不依賴OVS,即沒有OVS依然可以建立連接,此時就是out-of-band control
模式,這種模式下不需要”hidden” flows
但是在in-band control
模式下,TCP連接的建立依賴OVS控制的網絡,但此時OVS依賴OpenFLow控制器下發的flows才能正常工作,沒法建立TCP連接也就無法下發flows,這就產生矛盾了,因此需要存在一些”hidden” flows,這些”hidden” flows保證了TCP連接能夠正常建立。關於in-band control
詳細介紹,參考OVS官方文檔Design Decisions In Open vSwitch 中In-Band Control部分
“hidden” flows優先級高於OpenFlow flows,它們不需要手動設置。可以使用ovs-appctl
查看這些flows,下面命令輸出內容包括OpenFlow flows
,"hidden" flows
ovs-appctl bridge/dump-flows <br>
- datapath flows
datapath flows是datapath
內核模塊維護的flows,由內核模塊維護意味着我們並不需要去修改管理它。與OpenFlow flows不同的是,它不支持優先級,並且只有一個表,這些特點使它非常適合做緩存。與OpenFlow一樣的是它支持通配符,也支持指令集(多個action)
datapath flows可以來自用戶空間ovs-vswitchd
緩存,也可以是datapath內核模塊進行MAC地址學習到的flows,這取決與OVS是作為SDN交換機,還是像Linux Bridge那樣只是一個簡單基於MAC地址學習的二層交換機
幾種flows對比
我們可以修改和配置的是OpenFlow flows。datapath flow和”hidden” flows由OVS自身管理,我們不必去修改它。當然,調試場景下還是可以使用工具修改的
flows命令行工具
介紹下上面三種flows管理工具,不具體說明,具體使用可以查看相關man手冊
-
ovs-ofctl dump-flows <br>
打印指定網橋內的所有OpenFlow flows,可以存在多個流表(flow tables),按表順序顯示。不包括”hidden” flows。這是最常用的查看flows命令,當然這條命令對所有OpenFlow交換機都有效,不單單是OVS -
ovs-appctl bridge/dump-flows <br>
打印指定網橋內所有OpenFlow flows,包括”hidden” flows,in-band control
模式下排錯可以用到 -
ovs-dpctl dump-flows [dp]
打印內核模塊中datapath flows,[dp]
可以省略,默認主機中只有一個datapathsystem@ovs-system
man手冊可以找到非常詳細的用法說明,注意ovs-ofctl
管理的是OpenFlow flows
OVS中管理工具的使用及區別
上面介紹了OVS用戶空間進程以及控制器和OpenFlow協議,這里說下相關的命令行工具的使用及區別
ovs-vsctl
ovs-vsctl
是一個管理或配置ovs-vswitchd
的高級命令行工具,高級是說其操作對用戶友好,封裝了對數據庫的操作細節。它是管理OVS最常用的命令,除了配置flows之外,其它大部分操作比如Bridge/Port/Interface/Controller/Database/Vlan等都可以完成
#添加網橋br0 ovs-vsctl add-br br0 #列出所有網橋 ovs-vsctl list-br #添加一個Port p1到網橋br0 ovs-vsctl add-port br0 p1 #查看網橋br0上所有Port ovs-vsctl list-ports br0 #獲取br0網橋的OpenFlow控制器地址,沒有控制器則返回空 ovs-vsctl get-controller br0 #設置OpenFlow控制器,控制器地址為192.168.1.10,端口為6633 ovs-vsctl set-controller br0 tcp:192.168.1.10:6633 #移除controller ovs-vsctl del-controller br0 #刪除網橋br0 ovs-vsctl del-br br0 #設置端口p1的vlan tag為100 ovs-vsctl set Port p1 tag=100 #設置Port p0類型為internal ovs-vsctl set Interface p0 type=internal #添加vlan10端口,並設置vlan tag為10,Port類型為Internal ovs-vsctl add-port br0 vlan10 tag=10 -- set Interface vlan10 type=internal #添加隧道端口gre0,類型為gre,遠端IP為1.2.3.4 ovs-vsctl add-port br0 gre0 -- set Interface gre0 type=gre options:remote_ip=1.2.3.4
ovsdb-tool
ovsdb-tool
是一個專門管理OVS數據庫文件的工具,不常用,它不直接與ovsdb-server
進程通信
#可以使用此工具創建並初始化database文件 ovsdb-tool create [db] [schema] #可以使用ovsdb-client get-schema [database]獲取某個數據庫的schema(json格式) #可以查看數據庫更改記錄,具體到操作命令,這個比較有用 ovsdb-tool show-log -m record 48: 2017-01-07 03:34:15.147 "ovs-vsctl: ovs-vsctl --timeout=5 -- --if-exists del-port tapcea211ae-10" table Interface row "tapcea211ae-10" (151f66b6): delete row table Port row "tapcea211ae-10" (cc9898cd): delete row table Bridge row "br-int" (fddd5e27): table Open_vSwitch row a9fc1666 (a9fc1666): record 49: 2017-01-07 04:18:23.671 "ovs-vsctl: ovs-vsctl --timeout=5 -- --if-exists del-port tap5b4345ea-d5 -- add-port br-int tap5b4345ea-d5 -- set Interface tap5b4345ea-d5 "external-ids:attached-mac=\"fa:16:3e:50:1b:5b\"" -- set Interface tap5b4345ea-d5 "external-ids:iface-id=\"5b4345ea-d5ea-4285-be99-0e4cadf1600a\"" -- set Interface tap5b4345ea-d5 "external-ids:vm-id=\"0aa2d71e-9b41-4c88-9038-e4d042b6502a\"" -- set Interface tap5b4345ea-d5 external-ids:iface-status=active" table Port insert row "tap5b4345ea-d5" (4befd532): table Interface insert row "tap5b4345ea-d5" (b8a5e830): table Bridge row "br-int" (fddd5e27): table Open_vSwitch row a9fc1666 (a9fc1666): ...
ovsdb-client
ovsdb-client
是ovsdb-server
進程的命令行工具,主要是從正在運行的ovsdb-server
中查詢信息,操作的是數據庫相關
#列出主機上的所有databases,默認只有一個庫Open_vSwitch ovsdb-client list-dbs #獲取指定數據庫的schema信息 ovsdb-client get-schema [DATABASE] #列出指定數據庫的所有表 ovsdb-client list-tables [DATABASE] #dump指定數據庫所有數據,默認dump所有table數據,如果指定table,只dump指定table數據 ovsdb-client dump [DATABASE] [TABLE] #監控指定數據庫中的指定表記錄改變 ovsdb-client monitor DATABASE TABLE
ovs-ofctl
ovs-ofctl
是專門管理配置OpenFlow交換機的命令行工具,我們可以用它手動配置OVS中的OpenFlow flows,注意其不能操作datapath flows和”hidden” flows
#查看br-tun中OpenFlow flows ovs-ofctl dump-flows br-tun #查看br-tun端口信息 ovs-ofctl show br-tun #添加新的flow:對於從端口p0進入交換機的數據包,如果它不包含任何VLAN tag,則自動為它添加VLAN tag 101 ovs-ofctl add-flow br0 "priority=3,in_port=100,dl_vlan=0xffff,actions=mod_vlan_vid:101,normal" #對於從端口3進入的數據包,若其vlan tag為100,去掉其vlan tag,並從端口1發出 ovs-ofctl add-flow br0 in_port=3,dl_vlan=101,actions=strip_vlan,output:1 #添加新的flow: 修改從端口p1收到的數據包的源地址為9.181.137.1,show 查看p1端口ID為100 ovs-ofctl add-flow br0 "priority=1 idle_timeout=0,in_port=100,actions=mod_nw_src:9.181.137.1,normal" #添加新的flow: 重定向所有的ICMP數據包到端口 p2 ovs-ofctl add-flow br0 idle_timeout=0,dl_type=0x0800,nw_proto=1,actions=output:102 #刪除編號為 100 的端口上的所有流表項 ovs-ofctl del-flows br0 "in_port=100"
ovs-vsctl
是一個綜合的配置管理工具,ovsdb-client
傾向於從數據庫中查詢某些信息,而ovsdb-tool
是維護數據庫文件工具
文章地址http://www.isjian.com/openstack/openstack-base-use-openvswitch/
參考文章
https://www.sdxcentral.com/cloud/open-source/definitions/what-is-open-vswitch/
http://openvswitch.org/features/
https://www.ibm.com/developerworks/cn/cloud/library/1401_zhaoyi_openswitch/
http://openvswitch.org/slides/OpenStack-131107.pdf
http://horms.net/projects/openvswitch/2010-10/openvswitch.en.pdf
http://benpfaff.org/papers/ovs.pdf
https://networkheresy.com/category/open-vswitch/