虛擬交換機(OVS)之結構印象


當拿到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。從整體上了解了這些后,

  1. 就可以從原理上探究每一部分的大致組成以及實現過程。
  2. 配置OVS,發送和接收一個數據報文,追蹤其流程,觀察處理過程。
  3. 回顧總結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的第一步的總體印象的分析。后續的第二篇會進一步跟蹤配置和數據包的處理流程,詳細分析代碼的邏輯。期待下一篇吧。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM