當拿到OVS這么大一個工程的時候,如何理解他的組織、實現的功能、數據結構的創新,對於這個從0到1的過程,該如何一步步深入呢?
首先,我覺得直接看架構也好,看文件目錄也好,都是比較容易理解全局的辦法。
那就先看看文件的組織:

這些顯示的是文件夾的目錄,從目錄中可以看出有window相關的,也有xenserver相關的,說明OVS不光支持Linux,還支持別的平台。
然后瀏覽一下其他的目錄,根據之前的了解,有datapath,include,lib,ofproto,ovn,ovsdb,vswitchd,vetp這幾個重要的目錄,其他的如m4是跟編譯相關的。除了目錄之外,就是一些配置安裝說明文件。但是還是要吐槽一下,OVS的功能划分在文件組織上非常混沌,干脆是一坨直接丟在一起,比如lib目錄,跟DPDK的目錄組織差不少。DPDK的目錄可以借機出鏡一下:

所以,大致梳理一下的話,根據之前的了解
- datapath實現的是一個內核快速匹配轉發模塊。
- lib目錄應該實現的是很多算法、結構之類的東西,如hash,log等。
- ofproto目錄應該實現的是一個中間層,這也是重點要分析的地方,因為現在還看不出作用來。
- ovn是一個虛擬網絡的平台,應該不是ovs的必備組件。最后再看。
- ovsdb是ovs的數據庫,這個目錄實現的是server,client的一些東西,還有必要的接口。
- vswitchd是交換機實現的目錄,然鵝,里面的文件不多,網橋的實現。也算是核心了。
- vtep 是VxLAN隧道終結點設備,VxLAN是一種UDP隧道,多見於數據中心網絡實現overlay的網絡虛擬化。
從目錄上看,順帶查找一些資料,基本能獲得以上這些信息。另外此處先誕生一個疑問:
OVS是能夠用DPDK進行加速的,繞開內核,要修改的部分還不少,那么他們在目錄的哪里呢?實際上就是在lib這個大雜燴中-_-||
然后再從架構上看一下的話,就如下面這張圖:

核心的組成部分主要有3個,vswitchd,ovsdb-server,datapath。從整體上了解了這些后,
- 就可以從原理上探究每一部分的大致組成以及實現過程。
- 配置OVS,發送和接收一個數據報文,追蹤其流程,觀察處理過程。
- 回顧總結OVS的設計。
既然如此,就開始從第一部分出發吧,先探究這三個部分的主要內容。
1. datapath
第一個選datapath入手,是因為datapath是內核模塊,關聯性更少一些。
既然是內核模塊,就先找到模塊的初始化入口,在datapath.c中的dp_init(),這是個__init函數,__init告訴編譯器這個函數只用於初始化。
static int __init dp_init(void)
{
int err;
BUILD_BUG_ON(sizeof(struct ovs_skb_cb) > FIELD_SIZEOF(struct sk_buff, cb));
pr_info("Open vSwitch switching datapath %s\n", VERSION);
err = compat_init();
if (err)
goto error;
err = action_fifos_init(); /* 初始化延遲操作的隊列 */
if (err)
goto error_compat_exit;
err = ovs_internal_dev_rtnl_link_register(); /* internal設備操作集及netlink操作集的注冊 */
if (err)
goto error_action_fifos_exit;
err = ovs_flow_init(); /* 流表初始化 */
if (err)
goto error_unreg_rtnl_link;
err = ovs_vport_init(); /* vport子系統初始化 */
if (err)
goto error_flow_exit;
err = register_pernet_device(&ovs_net_ops); /* 注冊namespace相關的ovs初始化 */
if (err)
goto error_vport_exit;
err = register_netdevice_notifier(&ovs_dp_device_notifier); /* 注冊設備通知 */
if (err)
goto error_netns_exit;
err = ovs_netdev_init(); /* 注冊netdev類型的設備操作集 */
if (err)
goto error_unreg_notifier;
err = dp_register_genl(); /* 注冊通用的netlink,共注冊了四種*/
if (err < 0)
goto error_unreg_netdev;
return 0;
從大致過程上說,在初始化完成后,就等待配置,如流表下發,添加端口等,然后就是等待報文匹配。
除此之外的datapath.c文件中主要就是注冊的四種netlink的操作實現。
2. vswitchd
vswitchd是虛擬交換機的守護進程,主要的實現在vswitchd目錄下,bridge.c。來看一下這個用戶態進程的啟動,vswitchd的的入口是在ovs-vswitchd.c的main()函數。
前面的解析參數和DPDK的初始化部分就不再說了,然后創建守護進程:
daemonize_start(true);
之后創建了unixctl服務器,並注冊命令:
retval = unixctl_server_create(unixctl_path, &unixctl);
if (retval) {
exit(EXIT_FAILURE);
}
unixctl_command_register("exit", "", 0, 0, ovs_vswitchd_exit, &exiting);
之后初始化網橋,bridge_init(remote);,主要工作就是
- 創建和數據庫的連接
- 注冊各種協議(如stp,bond,lacp)的命令和回調函數。
再之后,就是啟動網橋和網卡接收,循環等待退出
while (!exiting) {
memory_run();
if (memory_should_report()) {
struct simap usage;
simap_init(&usage);
bridge_get_memory_usage(&usage);
memory_report(&usage);
simap_destroy(&usage);
}
bridge_run();
unixctl_server_run(unixctl);
netdev_run();
memory_wait();
bridge_wait();
unixctl_server_wait(unixctl);
netdev_wait();
這里有個memory_run()的,是監控內存的使用的功能,贊一下這個東西,對於故障的監測蠻有用。然后是網橋,unixctl服務器,netdev運行。
這樣子初始化過后,用戶態的vswitchd就運行起來了。
但是這里需要分析一下軟件在實現上的組織,因為還有一個ofproto的層存在,那么他和vswitchd有啥關系呢?
ofproto庫真正實現了交換機邏輯。除此之外,還有兩個重要的庫,一個是netdev,一個是dpif。前者是對設備的抽象,后者則實現了流表的操作。
對於這幾個重要的庫的作用和實現,等到追蹤報文和配置流程的時候再仔細分析。
3. ovsdb-server
這一部分來說說數據庫,南向接口把數據配置到數據庫,然后數據庫通過socket與vswitchd通信,把配置信息發給vswitchd。
同樣,ovsdb-server的入口是在ovsdb-server.c中的main()函數。
server_config.remotes = &remotes;
server_config.config_tmpfile = config_tmpfile;
save_config__(config_tmpfile, &remotes, &db_filenames);
daemonize_start(false);
/* Load the saved config. */
load_config(config_tmpfile, &remotes, &db_filenames);
讀取配置文件,加載配置信息到數據庫中。然后創建ovsdb-server,並打開。
jsonrpc = ovsdb_jsonrpc_server_create();
shash_init(&all_dbs);
server_config.all_dbs = &all_dbs;
server_config.jsonrpc = jsonrpc;
perf_counters_init();
SSET_FOR_EACH (db_filename, &db_filenames) {
error = open_db(&server_config, db_filename);
if (error) {
ovs_fatal(0, "%s", error);
}
}
之后就創建了unixctl服務器,注冊了多個命令
retval = unixctl_server_create(unixctl_path, &unixctl);
if (retval) {
exit(EXIT_FAILURE);
}
那么這里注冊unixctl服務器是讓誰連接呢?vswitchd以及ovsdb。
最后又到了main死循環中,main_loop()。
在其中也是和vswitchd差不多,啟動unixctl服務器,啟動ovs-db server。主要的流程就這些咯。
至此,就把OVS的三大部分的主要組成說完了,這也完成了OVS的第一步的總體印象的分析。后續的第二篇會進一步跟蹤配置和數據包的處理流程,詳細分析代碼的邏輯。期待下一篇吧。
