openvswitch架構

一、Opevswitch總體架構

 

Openvswitch的架構網上有如下的圖表示:

 

 

 

每個模塊都有不同的功能

ovs-vswitchd 爲主要模塊,實現交換機的守護進程daemon

 

在Openvswitch所在的服務器進行ps aux可以看到以下的進程

root 1008 0.1 0.8 242948 31712 ? S<Ll Aug06 32:17 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監聽了一個本機的db.sock文件

 

openvswitch.ko爲Linux內核模塊,支持數據流在內核的交換

 

我們使用lsmod列舉加載到內核的模塊:

~# lsmod | grep openvswitch

openvswitch 66901 0

gre 13808 1 openvswitch

vxlan 37619 1 openvswitch

libcrc32c 12644 2 btrfs,openvswitch

 

既有Openvswitch.ko,也有

ovsdb-server 輕量級數據庫服務器,保存配置信息,ovs-vswitchd通過這個數據庫獲取配置信息

 

通過ps aux可以看到如下進程

root 985 0.0 0.0 21172 2120 ? S< Aug06 1:20 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

 

可以看出,ovsdb-server將配置信息保存在conf.db中,並通過db.sock提供服務,ovs-vswitchd通過這個db.sock從這個進程讀取配置信息。

/etc/openvswitch/conf.db是json格式的,可以通過命令ovsdb-client dump將數據庫結構打印出來。

 

數據庫結構包含如下的表格。

 

 

數據庫結構如下:

 

 

通過ovs-vsctl創建的所有的網橋,網卡,都保存在數據庫裏面,ovs-vswitchd會根據數據庫裏面的配置創建真正的網橋,網卡。

ovs-dpctl 用來配置switch內核模塊。

ovs-vsctl 查詢和更新ovs-vswitchd的配置。

ovs-appctl 發送命令消息,運行相關daemon。

ovs-ofctl 查詢和控制OpenFlow交換機和控制器。

 

二、Openvswitch的代碼結構

 

Openvwitch進行數據流交換的主要邏輯都是在ovs-vswitchd和openvswitch.ko裏面實現的。

 

 

ovs-vswitchd會從ovsdb-server讀取配置,然後調用ofproto層進行虛擬網卡的創建或者流表的操作。

Ofproto是一個庫,實現了軟件的交換機和對流表的操作。

Netdev層抽象了連接到虛擬交換機上的網絡設備。

Dpif層實現了對於流表的操作。

 

對於OVS來講,有以下幾種網卡類型

1). netdev: 通用網卡設備 eth0 veth

接收: 一個nedev在L2收到報文後回直接通過ovs接收函數處理,不會再走傳統內核協議棧.

發送: ovs中的一條流指定從該netdev發出的時候就通過該網卡設備發送

2). internal: 一種虛擬網卡設備

接收: 當從系統發出的報文路由查找通過該設備發送的時候,就進入ovs接收處理函數

發送: ovs中的一條流制定從該internal設備發出的時候,該報文被重新注入內核協議棧

3). gre device: gre設備. 不管用戶態創建多少個gre tunnel, 在內核態有且只有一個gre設備

接收: 當系統收到gre報文後,傳遞給L4層解析gre header, 然後傳遞給ovs接收處理函數

發送: ovs中的一條流制定從該gre設備發送, 報文會根據流表規則加上gre頭以及外層包裹ip,查找路由發送

 

 

在如上的代碼結構中,vswitchd中就是ovs-vswitchd的入口代碼,ovsdb就是ovsdb-server的代碼,ofproto即上述的中間抽象層,lib下面有netdev,dpif的實現,datapath裏面就是內核模塊openvswitch.ko的代碼。

 

三、ovs-vswitchd和openvswitch.ko的交互方式netlink

 

datapath 運行在內核態,ovs-vswitchd 運行在用戶態,兩者通過netlink 通信。

netlink 是一種靈活和強大的進程間通信機制(socket),甚至可以溝通用戶態和內核態。

netlink 是全雙工的。作爲socket,netlink 的地址族是AF_NETLINK(TCP/IP socket 的地址族是AF_INET)

目前有大量的通信場景應用了netlink,這些特定擴展和設計的netlink 通信bus,被定義爲family。比如NETLINK_ROUTE、NETLINK_FIREWALL、NETLINK_ARPD 等。

因爲大量的專用family 會佔用了family id,而family id 數量自身有限(kernel 允許32個);同時爲了方便用戶擴展使用,一個通用的netlink family 被定義出來,這就是generic netlink family。

 

要使用generic netlink,需要熟悉的數據結構包括genl_family、genl_ops 等。

 

 

下面寫一個generic netlink的簡單實例

定義family如下

  1. /* attributes */
  2. enum {
  3. DOC_EXMPL_A_UNSPEC,
  4. DOC_EXMPL_A_MSG,
  5. __DOC_EXMPL_A_MAX,
  6. };
  7. #define DOC_EXMPL_A_MAX (__DOC_EXMPL_A_MAX - 1)
  8. /* attribute policy */
  9. static struct nla_policy doc_exmpl_genl_policy[DOC_EXMPL_A_MAX + 1] = {
  10. [DOC_EXMPL_A_MSG] = { .type = NLA_NUL_STRING },
  11. };
  12. /* family definition */
  13. static struct genl_family doc_exmpl_gnl_family = {
  14. .id = GENL_ID_GENERATE,
  15. .hdrsize = 0,
  16. .name = "DOC_EXMPL",
  17. .version = 1,
  18. .maxattr = DOC_EXMPL_A_MAX,
  19. };

 

定義op如下

  1. /* handler */
  2. int doc_exmpl_echo(struct sk_buff *skb, struct genl_info *info)
  3. {
  4. /* message handling code goes here; return 0 on success, negative values on failure */
  5. }
  6. /* commands */
  7. enum {
  8. DOC_EXMPL_C_UNSPEC,
  9. DOC_EXMPL_C_ECHO,
  10. __DOC_EXMPL_C_MAX,
  11. };
  12. #define DOC_EXMPL_C_MAX (__DOC_EXMPL_C_MAX - 1)
  13. /* operation definition */
  14. struct genl_ops doc_exmpl_gnl_ops_echo = {
  15. .cmd = DOC_EXMPL_C_ECHO,
  16. .flags = 0,
  17. .policy = doc_exmpl_genl_policy,
  18. .doit = doc_exmpl_echo,
  19. .dumpit = NULL,
  20. };

 

註冊family 到generic netlink 機制

  1. int rc;
  2. rc = genl_register_family(&doc_exmpl_gnl_family);
  3. if (rc != 0)
  4. goto failure;

 

將操作註冊到family

  1. int rc;
  2. rc = genl_register_ops(&doc_exmpl_gnl_family, &doc_exmpl_gnl_ops_echo);
  3. if (rc != 0)
  4.     goto failure;

 

Datapath是如何使用netlink的呢?

 

在dp_init()函數(datapath.c)中,調用dp_register_genl()完成對四種類型的family 以及相應操作的註冊,包括datapath、vport、flow 和packet。

前三種family,都對應四種操作都包括NEW、DEL、GET、SET,而packet 的操作僅爲EXECUTE。

 

對於flow這個family的定義如下:

  1. static const struct nla_policy flow_policy[OVS_FLOW_ATTR_MAX + 1] = {
  2.     [OVS_FLOW_ATTR_KEY] = { .type = NLA_NESTED },
  3.     [OVS_FLOW_ATTR_ACTIONS] = { .type = NLA_NESTED },
  4.     [OVS_FLOW_ATTR_CLEAR] = { .type = NLA_FLAG },
  5. };
  6. static struct genl_family dp_flow_genl_family = {
  7.     .id = GENL_ID_GENERATE,
  8.     .hdrsize = sizeof(struct ovs_header),
  9.     .name = OVS_FLOW_FAMILY,
  10.     .version = OVS_FLOW_VERSION,
  11.     .maxattr = OVS_FLOW_ATTR_MAX,
  12.     SET_NETNSOK
  13. };

 

Flow相關的ops的定義如下:

  1. static struct genl_ops dp_flow_genl_ops[] = {
  2.     {
  3.         .cmd = OVS_FLOW_CMD_NEW,
  4.         .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */
  5.         .policy = flow_policy,
  6.         .doit = ovs_flow_cmd_new_or_set
  7.     },
  8.     {
  9.         .cmd = OVS_FLOW_CMD_DEL,
  10.         .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */
  11.         .policy = flow_policy,
  12.         .doit = ovs_flow_cmd_del
  13.     },
  14.     {
  15.         .cmd = OVS_FLOW_CMD_GET,
  16.         .flags = 0, /* OK for unprivileged users. */
  17.         .policy = flow_policy,
  18.         .doit = ovs_flow_cmd_get,
  19.         .dumpit = ovs_flow_cmd_dump
  20.     },
  21.     {
  22.         .cmd = OVS_FLOW_CMD_SET,
  23.         .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */
  24.         .policy = flow_policy,
  25.         .doit = ovs_flow_cmd_new_or_set,
  26.     },
  27. };

 

Ovs-vswitchd作爲客戶端如何使用netlink

 

Ovs-vswitchd 對於netlink 的實現,主要在lib/netlink-socket.c 文件中。

lib\dpif-provider.h定義了struct dpif_class {,包含一系列函數指針,例如open,close等。

真正的dpif_class有兩種

一個是dpif-netdev.c中定義的const struct dpif_class dpif_netdev_class = {

  1. const struct dpif_class dpif_netdev_class = {
  2.     "netdev",
  3.     dpif_netdev_init,
  4.     dpif_netdev_enumerate,
  5.     dpif_netdev_port_open_type,
  6.     dpif_netdev_open,
  7.     dpif_netdev_close,
  8.     dpif_netdev_destroy,
  9.     dpif_netdev_run,
  10.     dpif_netdev_wait,
  11.     dpif_netdev_get_stats,
  12.     dpif_netdev_port_add,
  13.     dpif_netdev_port_del,
  14.     dpif_netdev_port_query_by_number,
  15.     dpif_netdev_port_query_by_name,
  16.     NULL, /* port_get_pid */
  17.     dpif_netdev_port_dump_start,
  18.     dpif_netdev_port_dump_next,
  19.     dpif_netdev_port_dump_done,
  20.     dpif_netdev_port_poll,
  21.     dpif_netdev_port_poll_wait,
  22.     dpif_netdev_flow_flush,
  23.     dpif_netdev_flow_dump_create,
  24.     dpif_netdev_flow_dump_destroy,
  25.     dpif_netdev_flow_dump_thread_create,
  26.     dpif_netdev_flow_dump_thread_destroy,
  27.     dpif_netdev_flow_dump_next,
  28.     dpif_netdev_operate,
  29.     NULL, /* recv_set */
  30.     NULL, /* handlers_set */
  31.     dpif_netdev_pmd_set,
  32.     dpif_netdev_queue_to_priority,
  33.     NULL, /* recv */
  34.     NULL, /* recv_wait */
  35.     NULL, /* recv_purge */
  36.     dpif_netdev_register_dp_purge_cb,
  37.     dpif_netdev_register_upcall_cb,
  38.     dpif_netdev_enable_upcall,
  39.     dpif_netdev_disable_upcall,
  40.     dpif_netdev_get_datapath_version,
  41.     NULL, /* ct_dump_start */
  42.     NULL, /* ct_dump_next */
  43.     NULL, /* ct_dump_done */
  44.     NULL, /* ct_flush */
  45. };

 

一種是在dpif-netlink.c中,定義了const struct dpif_class dpif_netlink_class = {

  1. const struct dpif_class dpif_netlink_class = {
  2.     "system",
  3.     NULL, /* init */
  4.     dpif_netlink_enumerate,
  5.     NULL,
  6.     dpif_netlink_open,
  7.     dpif_netlink_close,
  8.     dpif_netlink_destroy,
  9.     dpif_netlink_run,
  10.     NULL, /* wait */
  11.     dpif_netlink_get_stats,
  12.     dpif_netlink_port_add,
  13.     dpif_netlink_port_del,
  14.     dpif_netlink_port_query_by_number,
  15.     dpif_netlink_port_query_by_name,
  16.     dpif_netlink_port_get_pid,
  17.     dpif_netlink_port_dump_start,
  18.     dpif_netlink_port_dump_next,
  19.     dpif_netlink_port_dump_done,
  20.     dpif_netlink_port_poll,
  21.     dpif_netlink_port_poll_wait,
  22.     dpif_netlink_flow_flush,
  23.     dpif_netlink_flow_dump_create,
  24.     dpif_netlink_flow_dump_destroy,
  25.     dpif_netlink_flow_dump_thread_create,
  26.     dpif_netlink_flow_dump_thread_destroy,
  27.     dpif_netlink_flow_dump_next,
  28.     dpif_netlink_operate,
  29.     dpif_netlink_recv_set,
  30.     dpif_netlink_handlers_set,
  31.     NULL, /* poll_thread_set */
  32.     dpif_netlink_queue_to_priority,
  33.     dpif_netlink_recv,
  34.     dpif_netlink_recv_wait,
  35.     dpif_netlink_recv_purge,
  36.     NULL, /* register_dp_purge_cb */
  37.     NULL, /* register_upcall_cb */
  38.     NULL, /* enable_upcall */
  39.     NULL, /* disable_upcall */
  40.     dpif_netlink_get_datapath_version, /* get_datapath_version */
  41. #ifdef __linux__
  42.     dpif_netlink_ct_dump_start,
  43.     dpif_netlink_ct_dump_next,
  44.     dpif_netlink_ct_dump_done,
  45.     dpif_netlink_ct_flush,
  46. #else
  47.     NULL, /* ct_dump_start */
  48.     NULL, /* ct_dump_next */
  49.     NULL, /* ct_dump_done */
  50.     NULL, /* ct_flush */
  51. #endif
  52. };

 

datapath 中對netlink family 類型進行了註冊,ovs-vswitchd 在使用這些netlink family 之前需要獲取它們的信息,這一過程主要在lib/dpif-netlink.c 文件(以dpif_netlink_class 爲例),dpif_netlink_init ()函數。

  1. static int
  2. dpif_netlink_init(void)
  3. {
  4.     static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
  5.     static int error;
  6.  
  7.     if (ovsthread_once_start(&once)) {
  8.         error = nl_lookup_genl_family(OVS_DATAPATH_FAMILY,
  9.                                       &ovs_datapath_family);
  10.         if (error) {
  11.             VLOG_ERR("Generic Netlink family '%s' does not exist. "
  12.                      "The Open vSwitch kernel module is probably not loaded.",
  13.                      OVS_DATAPATH_FAMILY);
  14.         }
  15.         if (!error) {
  16.             error = nl_lookup_genl_family(OVS_VPORT_FAMILY, &ovs_vport_family);
  17.         }
  18.         if (!error) {
  19.             error = nl_lookup_genl_family(OVS_FLOW_FAMILY, &ovs_flow_family);
  20.         }
  21.         if (!error) {
  22.             error = nl_lookup_genl_family(OVS_PACKET_FAMILY,
  23.                                           &ovs_packet_family);
  24.         }
  25.         if (!error) {
  26.             error = nl_lookup_genl_mcgroup(OVS_VPORT_FAMILY, OVS_VPORT_MCGROUP,
  27.                                            &ovs_vport_mcgroup);
  28.         }
  29.  
  30.         ovsthread_once_done(&once);
  31.     }
  32.  
  33.     return error;
  34. }

 

完成這些查找後,ovs-vswitchd 即可利用dpif 中的api,通過發出這些netlink 消息給datapath,實現對datapath 的操作

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章