一、整體思路
現(xiàn)在進(jìn)入正題,今天的分享從三個(gè)方面進(jìn)行:
匹配域相關(guān)的各個(gè)模塊簡(jiǎn)單分析。
安裝檢驗(yàn)和調(diào)試
演示結(jié)果。
相比在ovs源碼中添加自定義action,自定義匹配域顯得關(guān)系更為復(fù)雜凌亂一些。為了讓和匹配域相關(guān)的模塊條理更加清楚明了,我盡量將要提到的相關(guān)模塊關(guān)系化,防止漏掉和匹配域相關(guān)的部分。這里先給出總體架構(gòu)圖:
架構(gòu)圖中包含了將要分析的8大模塊,每一個(gè)里面都有和匹配相關(guān)的內(nèi)容,接下來(lái)會(huì)按照這個(gè)思路逐一分析。其實(shí)大家發(fā)現(xiàn),這和流表從控制器下發(fā)后,數(shù)據(jù)包進(jìn)入交換機(jī)的處理流程非常吻合,想必大家多多少少有一些認(rèn)識(shí)。
二、各個(gè)模塊分析
下來(lái)進(jìn)入分享的重點(diǎn)。按照?qǐng)D中給出的思路,各個(gè)模塊講解順序依次為:
1、匹配域定義
2、flowmod解析
3、用戶層表項(xiàng)插入
4、內(nèi)核層packet解析和匹配處理
5、Upcall接收和分類
6、用戶層查找匹配處理
7、表項(xiàng)和packet的下發(fā)操作
8、內(nèi)核層flow插入和packet執(zhí)行
9、其他
1、匹配域定義
Ovs匹配域是基于OpenFlow協(xié)議的,因此,如果要添加一個(gè)新的匹配域,需要延續(xù)OF協(xié)議定義一個(gè)匹配域的邏輯,這樣拓展出的新匹配才能較為容易的和其他OF已經(jīng)定義的匹配域兼容起來(lái),同時(shí)保障OVS的匹配處理邏輯不發(fā)生改變。
1)目前OF支持兩種定義匹配域的格式,用的較多的是OXM格式,即TLV格式(類型,長(zhǎng)度和值)。我們之后的講解以TLV格式為基礎(chǔ)進(jìn)行。那么要想實(shí)現(xiàn)一個(gè)新的匹配域,代表類型的T和長(zhǎng)度的L比不少,他們定義在枚舉類型和宏定義中。
首先看枚舉類型,目前OF在1.3協(xié)議中已經(jīng)定義了40種匹配字段,它們枚舉值定義在include\\openflow\\Openflow-1.2.h中,部分截圖如下:
每一個(gè)匹配域有相應(yīng)ENUM值,從in_port的0到IPV6_EXTHDR的39,因此對(duì)于新的匹配域,需要以這種格式進(jìn)行添加即可,但ENUM值必須是目前還沒(méi)有定義過(guò)的值。
2)除了要添加枚舉值外,還需要添加一個(gè)TLV相關(guān)的宏定義。TLV頭部如下(TL部分,相當(dāng)于綁定了一個(gè)匹配字段的類型和長(zhǎng)度):
對(duì)于一個(gè)新匹配域,只需要按照上面格式進(jìn)行添加即可,注意4或是8指的是TLV中的L數(shù)值,表示匹配域值的長(zhǎng)度。如對(duì)于inport則是4字節(jié)。之后OVS對(duì)flowmod中匹配域解析就全依賴這個(gè)枚舉值和宏定義了,此外提一句,如果是在控制端也做匹配域添加,需要和這個(gè)枚舉值和TL格式對(duì)應(yīng)起來(lái)。
2、FlowMod消息解析
完成之前的新字段的TLV定義還遠(yuǎn)遠(yuǎn)不夠,即將等待我們的是,OVS如何能夠從Flowmod消息中準(zhǔn)確提取出匹配域,并且能無(wú)排斥的插入原生的OVS流表中。接下來(lái)分析一下flowmod消息解析模塊。
先上圖:
圖體現(xiàn)了大體思路: Flowmod消息的匹配域部分,最終是要按照TLV格式逐一解析出來(lái),然后經(jīng)過(guò)一系列依賴性和重復(fù)性檢測(cè)等,最后才能將匹配域部分完整的解析放置在match結(jié)構(gòu)體中。
Match是什么?是用來(lái)裝載從flowmod消息中解析出來(lái)的匹配域。先來(lái)看看match結(jié)構(gòu)體:
Match包含了flow和wc,前者裝載字段值,后者標(biāo)記字段掩碼(深入會(huì)發(fā)現(xiàn)wc也是用flow結(jié)構(gòu)體存儲(chǔ)掩碼)。Flow結(jié)構(gòu)體包含了匹配域所有字段類型,因此對(duì)于新的字段,需要在此結(jié)構(gòu)體中添加。
需要注意的是,匹配字段在flow中添加的前后位置要固定,因?yàn)楹竺嫣砑酉鄳?yīng)源碼時(shí)需要和這個(gè)位置一致。
2)說(shuō)完了match,那如何從flowmod的匹配域中逐一解析出每一個(gè)字段呢?(其主要思想體現(xiàn)在函數(shù)nx_pull_raw()中)
大體是這樣的,匹配域由多個(gè)TLV組成,每一個(gè)TLV是一個(gè)匹配字段。則OVS先會(huì)從flowmod匹配域中按照TLV中的L將每個(gè)OXM(TLV格式)切割出來(lái)。這樣是不是就解析完了呢,顯然不是,因?yàn)榍懈詈蟮暮戏ㄐ詿o(wú)法保障(如長(zhǎng)度是否符合定義,各個(gè)字段依賴是否正確等)。
這里就需要后面的工作了,通過(guò)分割出來(lái)的OXM的header(即TL部分),在匹配域哈希表mf_field(Hmap)中做哈希查找,然后查找到這個(gè)TL應(yīng)該對(duì)應(yīng)的mf_field結(jié)構(gòu)體。mf_field是OVS已經(jīng)聲明定義好的匹配域信息集合,包含依賴性,名字,長(zhǎng)度等信息,這些可以對(duì)分割出來(lái)的該字段進(jìn)行檢驗(yàn)。Ok,清楚了這些,下面給出匹配域字段解析的示意圖:
剛才提到字段信息的集合mf_field,其以數(shù)組形式定義在mf_fields中,我們需要在此處寫(xiě)入新字段的信息:
如上面這個(gè)是inport字段信息集合,可以看到它包含了名字,字段長(zhǎng)度和最開(kāi)始提到的匹配域定義的enum OXM_OF_IN_PORT。這里注意,包含的第一個(gè)屬性是mf_field的id號(hào),一個(gè)mf_field有一個(gè)id,其定義在mf_field_id枚舉類型中(對(duì)于新字段也需要在這里添加一個(gè)id,注意相對(duì)位置)。這個(gè)id號(hào)算是OVS自身識(shí)別匹配域類型的方式,之后匹配域合法性檢測(cè)會(huì)都會(huì)用到這個(gè)id號(hào)。
3)接下來(lái),會(huì)根據(jù)字段mf_field信息對(duì)分割的每個(gè)字段做依賴性檢測(cè)、重復(fù)性檢測(cè)和匹配域值的有效性檢測(cè)等。
A、依賴性檢測(cè):如當(dāng)設(shè)置ipv4匹配字段時(shí),會(huì)檢測(cè)match->flow的“二層協(xié)議匹配字段”是否已經(jīng)是ip協(xié)議。如果新添加匹配字段有依賴性限制,則需要在函數(shù)mf_are_prereqs_ok中添加case進(jìn)行檢測(cè)。
B、重復(fù)性檢測(cè):因?yàn)槠ヅ溆蜃侄问侵饌€(gè)解析的,為了防止當(dāng)前字段類型已經(jīng)在之前存在過(guò),則需要進(jìn)行重復(fù)性檢測(cè),對(duì)于新的字段,需要在函數(shù)mf_is_all_wild()添加代碼進(jìn)行檢測(cè)。
C、匹配域值的有效性檢測(cè):對(duì)于一些匹配字段值是有規(guī)定的,如inport號(hào)是否大于最大范圍等,對(duì)于新字段也需要在函數(shù)mf_is_value_valid()中完成檢測(cè)。
檢測(cè)完就可以安安心心的將解析的每個(gè)字段值賦給match結(jié)構(gòu)體了,賦值時(shí)會(huì)分有掩碼和無(wú)掩碼情況,也需要添加相應(yīng)新字段源碼。
其實(shí),令人欣慰的是,對(duì)于一個(gè)新字段需要在各處添加源碼,看似繁雜,也基本就是照別的字段源碼格式多寫(xiě)一個(gè)case的事情,照貓畫(huà)虎也算是是個(gè)好方法。
3、流表項(xiàng)插入
完成flowmod的匹配域解析,那么剩下的就是依照f(shuō)lowmod要求進(jìn)行流表項(xiàng)刪除、添加等操作,這里對(duì)于一個(gè)新字段無(wú)需源碼改動(dòng)。
OVS有很多保障性能的方法,這里就有一處,簡(jiǎn)答提一下:Ovs定義了一個(gè)重要結(jié)構(gòu)體cls_rule,其與匹配域信息、priority信息等相關(guān),且cls_rule關(guān)聯(lián)一個(gè)相應(yīng)的流表項(xiàng)。當(dāng)ovs向流表中插入新表項(xiàng)時(shí),不是以表項(xiàng)全部?jī)?nèi)容進(jìn)行重復(fù)性檢測(cè),而是通過(guò)cls_rule在分類器cls_calssifier中進(jìn)行查找,這種對(duì)流表項(xiàng)分類查找方法可以大大提高工作效率,完成新表項(xiàng)的添加或是更新。
4、內(nèi)核層packet解析和匹配處理
用戶層表項(xiàng)解析與插入告一段落,下來(lái)就是當(dāng)數(shù)據(jù)包進(jìn)入交換機(jī)時(shí),如何完成packet解析與匹配處理。(核心代碼位于datapath文件夾下,數(shù)據(jù)包頭解析和匹配旅程從ovs_vport_receive()開(kāi)始)
我們知道,ovs為了提高效率,數(shù)據(jù)包會(huì)先在內(nèi)核層datapath進(jìn)行流表項(xiàng)匹配處理,對(duì)于匹配失敗,或者是匹配到表項(xiàng)的action為發(fā)向用戶層時(shí),才會(huì)去用戶層繼續(xù)查找匹配。對(duì)于在用戶層匹配成功的數(shù)據(jù)包會(huì)按照表項(xiàng)action相應(yīng)處理,并向內(nèi)核層下發(fā)一條匹配到的表項(xiàng),方便以后類似數(shù)據(jù)包直接在內(nèi)核層完成匹配轉(zhuǎn)發(fā)。
這個(gè)過(guò)程將是要一一解釋的關(guān)鍵點(diǎn),無(wú)不和匹配域息息相關(guān)。先來(lái)說(shuō)說(shuō)數(shù)據(jù)包進(jìn)入ovs內(nèi)核層的處理過(guò)程。
1)當(dāng)一個(gè)OVS端口接收到一個(gè)數(shù)據(jù)包,不是將整個(gè)數(shù)據(jù)包在內(nèi)核層的流表中匹配查找,這樣效率低下,而是需要對(duì)此數(shù)據(jù)包頭字段進(jìn)行解析,將解析出來(lái)的各個(gè)匹配字段值和端口號(hào)一起構(gòu)造成查詢key,然后用key在流表中進(jìn)行匹配查找。
查詢key,它是一個(gè)sw_flow_key結(jié)構(gòu)體,如下,包含了各個(gè)匹配字段的類型,對(duì)于新字段也需要在這里進(jìn)行添加。
此外,需要調(diào)用函數(shù)key_extract()依次從包頭中提取各個(gè)字段放入key中。如果你構(gòu)造了一個(gè)數(shù)據(jù)包新協(xié)議字段,就需要在這個(gè)函數(shù)中提取相應(yīng)包頭字段賦值給key即可,包頭提取都是對(duì)linux的結(jié)構(gòu)體操作,很方便快捷。
2)有了key,那就是內(nèi)核層流表項(xiàng)匹配查找的事情了。由于此次分享圍繞匹配域展開(kāi),內(nèi)核中流表匹配查找階段,不涉及具體的匹配字段,也無(wú)需做修改添加,因此不具體分析匹配查找流表項(xiàng)的具體過(guò)程。
查找結(jié)果無(wú)非兩種,查找成功和查找失敗。查找失敗則構(gòu)造upcall上交用戶層繼續(xù)查找處理,但這里需要注意,即使查找到也可能面臨上交處理。因?yàn)橛幸恍゛ction無(wú)法在內(nèi)核層執(zhí)行,這種action在下發(fā)到內(nèi)核層時(shí)已經(jīng)標(biāo)記為OVS_ACTION_ATTR_USERSPACE類型,此時(shí)也需要上交用戶層進(jìn)一步匹配處理。
上交用戶層時(shí)(主要體現(xiàn)在queue_userspace_packet函數(shù)中),會(huì)構(gòu)造上交的數(shù)據(jù)包user_skb(skb_buf結(jié)構(gòu)體),然后通過(guò)generic netlink通信機(jī)制上交給用戶層。
結(jié)構(gòu)體skb_buf可以簡(jiǎn)單理解為這樣的結(jié)構(gòu):Netlink頭部+Attr+Attr+...,Attr是type+len+data結(jié)構(gòu)。Attr主要分為三個(gè)類型信息:
?key:即由包頭等構(gòu)造的查詢key,必不可少,數(shù)據(jù)類型type為OVS_PACKET_ATTR_KEY
?userdata:用于匹配成功卻仍要走slow-path的數(shù)據(jù)包,標(biāo)記了action參數(shù)(如原因),數(shù)據(jù)類型type為OVS_PACKET_ATTR_USERDATA
?packet:顧名思義,原始數(shù)據(jù)包。類型type為OVS_PACKET_ATTR_PACKET。
注意,在這里,有兩部分內(nèi)容和匹配域相關(guān),添加新匹配域時(shí)候就需要在此處修改源碼:
?A是對(duì)于待上交key中含有的各個(gè)字段計(jì)算總長(zhǎng)度(key_attr_size())
?B上傳數(shù)據(jù)user_skb中的key包含很多匹配字段。因此新字段也要從key中提取出來(lái)加入到待傳輸?shù)接脩魧拥臄?shù)據(jù)體中(函數(shù)ovs_nla_put_flow()),提取時(shí)會(huì)用到各個(gè)匹配域數(shù)據(jù)的類型(enum ovs_key_attr枚舉類型中定義)。
5、Upcall接收和分類
到這里,已經(jīng)完成和匹配域相關(guān)的多大半內(nèi)容,思路已經(jīng)比較清晰,后面將加快進(jìn)度。
上面說(shuō)到,內(nèi)核層會(huì)封裝含有key、packet和action參數(shù)等內(nèi)容的upcall消息上交用戶層。那么用戶層接收到upcall之后直接匹配表項(xiàng)即可,為什么還要分類呢?(其主要體現(xiàn)在函數(shù)read_upcalls()(ofproto-dpif-upcalls.c))。
先給一張圖:
可以看到,用戶層的upcall結(jié)構(gòu)體有dupcall和miss兩個(gè)成員,這就和ovs性能提升密切相關(guān)了。OVS將具有相同key的upcall歸為一類,管理映射到同一個(gè)miss中。這樣就完成了相似packet的分類工作,便于后期統(tǒng)一匹配處理,提高效率。
在上面這個(gè)過(guò)程中,需要從key提取出flow進(jìn)行哈希查找和分類。Flow就是前面講解到的用戶層用于表示匹配域的結(jié)構(gòu)體,OVS調(diào)用函數(shù)flow_extract()函數(shù)從packet與md(metadata元數(shù)據(jù))中解析并構(gòu)造flow賦值給miss->flow,在這里別忘了添加相應(yīng)解析函數(shù)。
其實(shí),分類還包括了對(duì)slow path原因的分類處理,因和匹配域無(wú)關(guān),就不詳述了
6、用戶層查找匹配處理
完成upcall前期接收和分類工作,下來(lái)就是匹配處理了(主要體現(xiàn)在函數(shù)handle_upcalls()(ofproto-dpif-upcall.c))。
這里只有一處和新匹配域添加相關(guān)(odp_flow_key_from_flow__()函數(shù)),因此主要強(qiáng)調(diào)其工作原理。OVS會(huì)先分批(之前提到的,劃分為同一個(gè)miss的數(shù)據(jù)包)完成用戶層流表匹配查找,然后得到流表項(xiàng)action,并將用戶層action翻譯為內(nèi)核層odp_action,并對(duì)屬于slow_path的action數(shù)據(jù)包做特殊標(biāo)記處理(miss.xout->slow),尤其對(duì)部分slow_path中slow_action的做help標(biāo)記。之后就可以下發(fā)查找到的表項(xiàng)到內(nèi)核層了,并將數(shù)據(jù)包發(fā)到內(nèi)核層去執(zhí)行流表項(xiàng)的action。
這個(gè)過(guò)程很合情合理,但標(biāo)記做什么用呢?因?yàn)閿?shù)據(jù)包匹配到的流表項(xiàng),其action執(zhí)行只能通過(guò)慢通道處理(最典型的就是Controller action,甚至是因?yàn)閍ction過(guò)多或是數(shù)據(jù)量太大),因此標(biāo)記后,就會(huì)將這些含有slow_path action的表項(xiàng)和packet 直接在用戶層完成特殊處理,這基本和內(nèi)核層關(guān)系就不大了,效率自然也不會(huì)高。
7、表項(xiàng)和Packet的下發(fā)操作
接下來(lái)的工作,就是將表項(xiàng)下發(fā)到內(nèi)核層,并將packet通過(guò)netlink機(jī)制下發(fā)到內(nèi)核層去執(zhí)行action(主要體現(xiàn)在函數(shù)dpif_operate()中)。
由于之前提到的slow-path原因,OVS會(huì)采用兩種形式下發(fā),一種是和slow-path無(wú)關(guān)的統(tǒng)一處理下發(fā),一種是和slow-path相關(guān)的單獨(dú)特殊處理。
1)統(tǒng)一下發(fā)處理較為簡(jiǎn)單,就是批量以廣播形式通過(guò)netlink機(jī)制下發(fā)到內(nèi)核層,完成流表項(xiàng)在內(nèi)核層的安裝和packet在內(nèi)核層action的執(zhí)行。這里需要注意的是,如果自定義的新匹配域?qū)儆趍etadata類型,如inport這種,那么需要在odp_key_from_pkt_metadata()函數(shù)中,實(shí)現(xiàn)將元數(shù)據(jù)內(nèi)容的取出放入request緩存后等待下發(fā)的功能。
2)特殊處理:對(duì)于一個(gè)需要slow-path處理的packet,其所有動(dòng)作actions本應(yīng)在用戶層執(zhí)行(即在odp_execute_actions__()函數(shù)),但是執(zhí)行到OVS_ACTION_ATTR_OUTPUT類型action時(shí),不言而喻其最后需要發(fā)送到內(nèi)核層完成轉(zhuǎn)發(fā)。那么這種含有slow_path的流表項(xiàng)是否需要下發(fā)到內(nèi)核層?還記得之前的action翻譯嗎,這種表項(xiàng)會(huì)將action翻譯為OVS_ACTION_ATTR_USERSPACE下發(fā)到內(nèi)核層中。如下,用戶層表項(xiàng)到內(nèi)核層表項(xiàng):
請(qǐng)注意,特殊處理中如果牽扯到set_field action,就需要在odp_execute_set_actio()添加新匹配域的set函數(shù)。
8、內(nèi)核層flow插入和packet執(zhí)行
轉(zhuǎn)了一圈,又回到了內(nèi)核層。在內(nèi)核層完成flow的插入和packet action執(zhí)行工作基本就大功告成了。這里面的原理比較簡(jiǎn)單,因此只提及在表項(xiàng)插入過(guò)程中與匹配域相關(guān)的地方。
OVS主要在用戶層下發(fā)的表項(xiàng)數(shù)據(jù)中,對(duì)含有的匹配字段值進(jìn)行解析和字段有效性檢驗(yàn),完成表項(xiàng)插入。匹配字段解析中包含字段長(zhǎng)度解析(ovs_key_lens()函數(shù))和字段掩碼解析(ovs_key_from_nlattrs()函數(shù)),有效性檢驗(yàn)(match_validate()函數(shù))主要完成了匹配字段是否全初始化檢驗(yàn)、掩碼和值的一致性檢驗(yàn)等,對(duì)于新匹配域,以上幾個(gè)函數(shù)需要修改。
9、其他
圍繞著OVS匹配域有關(guān)的處理流程,終于分析完了從表項(xiàng)解析、插入、匹配,執(zhí)行等一系列過(guò)程。當(dāng)然,新的匹配域可能還不能很好的運(yùn)作,因?yàn)檫€差打印顯示和手動(dòng)插入等功能。這部分比較獨(dú)立,簡(jiǎn)單提及函數(shù)即可。
打印密切相關(guān):
miniflow_extract()
flow_format()
odp_flow_key_attr_len()
ovs_key_attr_to_string()
format_odp_key_attr()
其他一些:
mf_set_flow_value()(lib/meta-flow.c)
mf_get_value()(lib/meta-flow.c)
nx_put_raw()(nx-match.c)
parse_odp_key_mask_attr函數(shù)()(lib\\odp-util.c)
序號(hào)數(shù)FLOW_WC_SEQ
二、安裝檢驗(yàn)和調(diào)試
對(duì)于添加一個(gè)自定義匹配域,源碼修改就算完成了,雖然比較繁瑣,但是每一處改動(dòng)不大,基本照貓畫(huà)虎即可。安裝過(guò)程簡(jiǎn)單,采用常規(guī)安裝OVS的方法即可。
如果安裝后,采用ovs-ofctl命令可以正常添加一條帶有自定義匹配域的流表項(xiàng),并且數(shù)據(jù)包可以成功如愿以償?shù)钠ヅ涞竭@條表項(xiàng),那基本就大功告成了。
如果安裝失敗或是匹配不能按照預(yù)想的效果,需要進(jìn)行調(diào)試。調(diào)試一般采用兩種方法,查看log信息和gdb工具調(diào)試:
1)log信息:匹配域的添加涉及用戶層和內(nèi)核層,ovs在用戶層提供了相應(yīng)log函數(shù)VLOG_WARN、VLOG_INFO、VLOG_DBG等,直接使用即可,用戶層log信息一般位于/usr/local/var/log/openvswitch/ovs-vswitchd.log中查看;
內(nèi)核層可以使用printk等函數(shù)添加log,并在/var/log/kern.log中查看即可。
2)采用dbg方式,比較準(zhǔn)確高級(jí)的。
三、演示結(jié)果
說(shuō)了這么多,沒(méi)有實(shí)驗(yàn)結(jié)果都是不可靠的。因此,我下發(fā)了一條流表項(xiàng),包含了一個(gè)新的匹配域,并且成功匹配到了數(shù)據(jù)包,達(dá)到預(yù)期效果。可以用ofctl查看用戶層表項(xiàng),用dpctl查看內(nèi)核層表項(xiàng)。
為了能夠正確添加自定義匹配域,上文對(duì)于ovs匹配字段的執(zhí)行流程和基本原理做了分析說(shuō)明
審核編輯:郭婷
評(píng)論