李江寶
(江蘇自動(dòng)化研究所,江蘇連云港 222061)
DDS(Data Distribution Service)是對(duì)象管理組織OMG發(fā)布的關(guān)于分布式實(shí)時(shí)系統(tǒng)中數(shù)據(jù)發(fā)布的規(guī)范,該規(guī)范標(biāo)準(zhǔn)化了分布式實(shí)時(shí)系統(tǒng)中數(shù)據(jù)發(fā)布、傳遞和接收的接口和行為,定義了以數(shù)據(jù)為中心的發(fā)布-訂閱機(jī)制,提供了一個(gè)與平臺(tái)無(wú)關(guān)的數(shù)據(jù)模型。DDS將分布式網(wǎng)絡(luò)中傳輸?shù)臄?shù)據(jù)定義為主題(Topic),將數(shù)據(jù)的產(chǎn)生和接收對(duì)象分別定義為發(fā)布者(Publisher)和訂閱者(Subscriber),從而構(gòu)成數(shù)據(jù)的發(fā)布/訂閱傳輸模型。各個(gè)節(jié)點(diǎn)在邏輯上無(wú)主從關(guān)系,點(diǎn)與點(diǎn)之間都是對(duì)等關(guān)系,通信方式可以是點(diǎn)對(duì)點(diǎn)、點(diǎn)對(duì)多、多對(duì)多等,在QoS的控制下建立連接,自動(dòng)發(fā)現(xiàn)和配置網(wǎng)絡(luò)參數(shù)[1]。RTPS(Real-Time Publish-Subscribe)協(xié)議是對(duì)DDS規(guī)范的實(shí)時(shí)發(fā)布-訂閱協(xié)議規(guī)范,該規(guī)范確保使用一個(gè)供應(yīng)商的DDS實(shí)現(xiàn)發(fā)布的某個(gè)主題的信息可供相同或不同供應(yīng)商DDS實(shí)現(xiàn)的一個(gè)或多個(gè)訂閱者使用。
基于可提供低延遲、高吞吐量、可控傳輸性能、數(shù)據(jù)為中心等優(yōu)點(diǎn),目前DDS已越來(lái)越多的應(yīng)用于航空、國(guó)防、工業(yè)自動(dòng)化等多個(gè)領(lǐng)域[2-4]。隨著DDS在各行業(yè)中的應(yīng)用越來(lái)越多,產(chǎn)品開(kāi)發(fā)調(diào)試過(guò)程中需要輔助開(kāi)發(fā)調(diào)試,測(cè)試工具。目前產(chǎn)品中使用較多的DDS中間件有RTI-DDS、OpenDDS等,這些中間件雖然提供了一些工具輔助進(jìn)行調(diào)試診斷、排除故障等,但是購(gòu)買(mǎi)費(fèi)用較高,需要合適的工具進(jìn)行定制。
以RTI-DDS為例,其軟件套件中雖然提供了通信報(bào)文解析分析工具,但是只能基于結(jié)構(gòu)化的IDL接口定義,對(duì)于序列化報(bào)文中的自定義數(shù)據(jù)結(jié)構(gòu)無(wú)法進(jìn)行解析,而實(shí)際項(xiàng)目使用中,需通信的信息結(jié)構(gòu)數(shù)量較多,不可能逐一定義為IDL接口,更多的是使用序列化傳輸數(shù)據(jù)方式,在序列化傳輸?shù)臄?shù)據(jù)之下應(yīng)用協(xié)議,由收發(fā)雙方程序根據(jù)協(xié)議組織或解析。這種使用方式下由DDS供應(yīng)商提供的工具就無(wú)法解析序列化數(shù)據(jù)中的應(yīng)用協(xié)議結(jié)構(gòu);并且開(kāi)發(fā)商提供的通信報(bào)文解析分析工具也不支持二次開(kāi)發(fā),無(wú)法將網(wǎng)絡(luò)報(bào)文中的序列化應(yīng)用數(shù)據(jù)傳遞出來(lái),由開(kāi)發(fā)人員分析其中的自定義數(shù)據(jù)是否符合協(xié)議。
應(yīng)用數(shù)據(jù)協(xié)議報(bào)文解析工具對(duì)于開(kāi)發(fā)調(diào)試以及軟件接口測(cè)試是必需的,尤其是大系統(tǒng)中多個(gè)不同廠家生產(chǎn)的設(shè)備之間使用DDS通信時(shí),為進(jìn)行接口對(duì)接更需要一個(gè)合適的工具來(lái)輔助調(diào)試開(kāi)發(fā)。RTPS協(xié)議雖然是基于TCP/IP的網(wǎng)絡(luò)協(xié)議,但是為了支持規(guī)范要求的各項(xiàng)功能,實(shí)際網(wǎng)絡(luò)傳輸?shù)膽?yīng)用數(shù)據(jù)之前封裝了不定長(zhǎng)的復(fù)雜控制數(shù)據(jù),無(wú)法采用傳統(tǒng)的網(wǎng)絡(luò)抓包分析方法來(lái)輔助調(diào)試分析報(bào)文內(nèi)容。開(kāi)發(fā)人員根據(jù)RTPS規(guī)范開(kāi)發(fā)一套解析軟件又不現(xiàn)實(shí),而開(kāi)源網(wǎng)絡(luò)封包軟件Wireshark支持對(duì)RTPS協(xié)議的封包解析,可以從中提取出相應(yīng)的協(xié)議解析代碼嵌入到應(yīng)用數(shù)據(jù)協(xié)議解析工具中。
Wireshark是跨平臺(tái)的開(kāi)源網(wǎng)絡(luò)封包分析軟件,在GNU GPL通用許可證的保障范圍內(nèi),使用者可以免費(fèi)取得軟件與源代碼,并擁有針對(duì)其源代碼修改及定制化的權(quán)利。其底層使用Winpcap/libpcap作為網(wǎng)絡(luò)數(shù)據(jù)包捕獲接口,可實(shí)時(shí)顯示數(shù)據(jù)包的詳細(xì)協(xié)議信息,并支持其他常用抓包軟件的包數(shù)據(jù)文件導(dǎo)入和導(dǎo)出。其支持700多種協(xié)議的解析,幾乎包含所有的公開(kāi)網(wǎng)絡(luò)協(xié)議,并可支持自定義協(xié)議解析器的擴(kuò)展。Wireshark的系統(tǒng)結(jié)構(gòu)如圖1所示[5]。
網(wǎng)絡(luò)數(shù)據(jù)包經(jīng)由WinPcap/libpcap捕獲之后,經(jīng)抓包引擎Dumpcap傳入接口模塊Capture中,Capture模塊將數(shù)據(jù)傳入數(shù)據(jù)包分析引擎Epan解析或者傳入Wiretap保存到磁盤(pán)中;GUI作為圖形界面,處理所有用戶的輸入/輸出;Core則負(fù)責(zé)將其他模塊組織起來(lái)。
網(wǎng)絡(luò)協(xié)議解析的核心代碼在Epan(Enhanced Packet ANalyzer)模塊,源碼在epan目錄下。其主要包括4個(gè)子模塊[6]:
Protocol-Tree:保存數(shù)據(jù)包的協(xié)議信息。Wireshark的協(xié)議結(jié)構(gòu)采用樹(shù)形結(jié)構(gòu),解析協(xié)議報(bào)文時(shí)只需從根節(jié)點(diǎn)通過(guò)函數(shù)句柄依次調(diào)用各層解析函數(shù)即可;
Dissectors:在epan/dissector目錄下的各種協(xié)議解析器。支持700多種協(xié)議解析,對(duì)于每種協(xié)議,解碼器都能識(shí)別出協(xié)議字段(field),并顯示出字段值(field value)。由于網(wǎng)絡(luò)協(xié)議種類很多,為了使協(xié)議和協(xié)議間層次關(guān)系明顯,對(duì)數(shù)據(jù)流里的各個(gè)層次的協(xié)議能夠逐層處理,Wireshark采用了協(xié)議樹(shù)的方式;
Dissector-Plugins:以插件形式實(shí)現(xiàn)的協(xié)議解析器,源碼在plugins目錄;
Display-Filters:顯示過(guò)濾引擎,源碼在epan/dfilter目錄。
Wireshark的協(xié)議解析開(kāi)發(fā)由內(nèi)置型和插件型構(gòu)成。插件型協(xié)議解析開(kāi)發(fā)相對(duì)容易編寫(xiě),因?yàn)樗恍枰老到y(tǒng)的整個(gè)框架結(jié)構(gòu)以及整個(gè)程序的詳細(xì)流程,只需要根據(jù)接口的相關(guān)說(shuō)明直接調(diào)用就可以。內(nèi)置型的協(xié)議解析開(kāi)發(fā)對(duì)比插件方式而言,需要了解Wireshark的組織架構(gòu)、哪些協(xié)議解析本身是以內(nèi)置型的方式開(kāi)發(fā)的、源文件存放位置、程序編譯和運(yùn)行信息,需要十分熟悉整個(gè)Wireshark的開(kāi)發(fā)。其在編寫(xiě)完解析器之后需要對(duì)整個(gè)工程進(jìn)行編譯,工作量較大。
雖然通過(guò)開(kāi)發(fā)應(yīng)用協(xié)議解析插件方式實(shí)現(xiàn)對(duì)應(yīng)用協(xié)議數(shù)據(jù)的解析,但考慮到使用時(shí)需要安裝整個(gè)Wireshark軟件并且通過(guò)上述分析Wireshark的工作原理可知:其采用協(xié)議樹(shù)逐層解析方式,各種協(xié)議解析器之間基本沒(méi)有相互關(guān)聯(lián),可以比較方便的將所需協(xié)議解析代碼從整體代碼中剝離出來(lái)。所以可采用將RTPS協(xié)議解析代碼剝離為獨(dú)立模塊嵌入?yún)f(xié)議解析軟件中使用的方式來(lái)達(dá)到協(xié)議解析目的,遠(yuǎn)遠(yuǎn)小于開(kāi)發(fā)Wireshark插件所需的工作量,而且還可以根據(jù)需要隨時(shí)修改。
Wireshark源代碼中實(shí)現(xiàn)對(duì)RTPS協(xié)議解析的代碼是Epan/dissectors目錄下的packet-rtps.h和packet-rtps.c兩個(gè)文件。由于Wireshark的dissectors采用的是協(xié)議樹(shù)逐層解析的方式,與其他網(wǎng)絡(luò)協(xié)議解析器無(wú)關(guān)聯(lián),并且RTPS協(xié)議是在TCP/IP層上進(jìn)行的封裝,所以通過(guò)將RTPS協(xié)議解析代碼從Wireshark剝離為獨(dú)立的模塊具備可行性。下面介紹如何將RTPS協(xié)議解析相關(guān)代碼剝離形成獨(dú)立于Wireshark的模塊。
由于Wireshark中使用了glib庫(kù)中的大量結(jié)構(gòu)作為基礎(chǔ)元素,特別是作為基礎(chǔ)數(shù)據(jù)類型、鏈表、字符串等,所以首先需要完成對(duì)該庫(kù)的處理。因?yàn)間lib庫(kù)是跨平臺(tái)的基礎(chǔ)庫(kù),支持Linux、Windows等多個(gè)平臺(tái)[7],并且提供編譯好的二進(jìn)制庫(kù),所以可直接下載使用,不需要?jiǎng)冸x移植等,只需要針對(duì)使用平臺(tái)下載對(duì)應(yīng)的頭文件和庫(kù)文件,使用時(shí)進(jìn)行鏈接即可。
Wireshark使用了GTK/Qt來(lái)開(kāi)發(fā)圖形界面,因?yàn)閮H需要實(shí)現(xiàn)網(wǎng)絡(luò)數(shù)據(jù)報(bào)文的解析,不需要圖形界面,所以與圖形界面相關(guān)的代碼等也不需要移植。但是源文件中跟界面顯示相關(guān)的代碼以及結(jié)構(gòu)體中的相關(guān)字段需要進(jìn)行去除處理。
Wireshark中的內(nèi)存管理模塊叫wmem,在epan/wmem目錄下。該模塊除使用了glib庫(kù)之外,不依賴于其他模塊,可以獨(dú)立出來(lái),所以可將其剝離并編譯為一個(gè)庫(kù)使用。
packet-rtps.h和packet-rtps.c兩個(gè)代碼文件包含的其他頭文件雖然不多,但是代碼中卻使用了Wireshark的很多基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)和輔助函數(shù),所以要實(shí)現(xiàn)這兩個(gè)代碼文件的剝離獨(dú)立,必須把其代碼中使用的相關(guān)數(shù)據(jù)結(jié)構(gòu)和輔助函數(shù)一起剝離獨(dú)立出來(lái),才能實(shí)現(xiàn)整個(gè)協(xié)議解析功能代碼的完全獨(dú)立。
代碼中使用的基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)主要有:frame-data,tvbuff-t、packet-info、proto-node等;
frame-data結(jié)構(gòu)用于存儲(chǔ)單個(gè)網(wǎng)絡(luò)報(bào)文,雖然該結(jié)構(gòu)中有較多成員,但基本都是基礎(chǔ)數(shù)據(jù)類型或glib中的數(shù)據(jù)類型,所以代碼剝離簡(jiǎn)單,不需特別處理。
packet-info 結(jié)構(gòu)中除基礎(chǔ)數(shù)據(jù)類型和glib數(shù)據(jù)類型外,還有一些其他外部數(shù)據(jù)結(jié)構(gòu)字段,如struct epan-column-info *cinfo,struct wtap-pkthdr *phdr等,由于在RTPS協(xié)議解析中并不需要,所以將這些結(jié)構(gòu)的字段直接去除。
proto-node為協(xié)議樹(shù)節(jié)點(diǎn),其中的field-info類型的字段在協(xié)議解析中并不需要,將其去除。
tvbuff-t結(jié)構(gòu)是具體的網(wǎng)絡(luò)數(shù)據(jù),struct tvb-ops *ops字段也不需要,直接去除。
將以上主要的結(jié)構(gòu)從Wireshark中剝離后,組合成一個(gè)協(xié)議解析模塊的獨(dú)立結(jié)構(gòu)定義頭文件public-defines.h,并包含到packet-rtps.h中。
協(xié)議解析中使用的輔助函數(shù)主要是從報(bào)文中提取不同類型數(shù)據(jù)的函數(shù)和一些異常處理函數(shù),主要有從報(bào)文中取不同類型數(shù)據(jù)的函數(shù)(其中N可以是8、16、32、64):
guintN tvb-get-guintN(tvbuff-t *tvb, const gint offset, const guint encoding);
guint32 tvb-get-ntohl(tvbuff-t *tvb, const gint offset);
guint16 tvb-get-ntohs(tvbuff-t *tvb, const gint offset);
以及檢測(cè)報(bào)文長(zhǎng)度的函數(shù):
gint tvb-reported-length-remaining(const tvbuff-t *tvb, const gint offset);
由于檢測(cè)報(bào)文長(zhǎng)度函數(shù)中使用了異常處理,所以還需將異常處理的except.h和except.c兩個(gè)文件一起進(jìn)行移植剝離。這兩個(gè)異常處理文件并未依賴其他文件,可以直接從代碼中剝離。
將以上關(guān)聯(lián)輔助函數(shù)從Wireshark中剝離組成單獨(dú)的協(xié)議解析模塊輔助函數(shù)頭文件和源文件。
由于RTPS協(xié)議解析代碼已經(jīng)從Wireshark中剝離,不需要協(xié)議樹(shù)的注冊(cè)等,所以將packet-rtps.c文件中的proto-register-rtps函數(shù)去除;
由于在數(shù)據(jù)結(jié)構(gòu)packet-info中已經(jīng)刪除了cinfo字段,所以解析代碼中所有與其相關(guān)的代碼都需去掉。
packet-rtps.c文件中以proto-tree-和proto-item-開(kāi)頭的所有的函數(shù)調(diào)用也都可以刪除。
最后對(duì)packet-rtps.c以及依賴代碼文件中與剝離協(xié)議封裝頭無(wú)關(guān)的代碼進(jìn)行注釋,去除對(duì)其他文件的依賴,最終實(shí)現(xiàn)協(xié)議解析代碼從Wireshark中的剝離。
剝離出的代碼主要包含packet-rtps.c、packet-rtps.h、wmem模塊,以及定義基礎(chǔ)結(jié)構(gòu)的頭文件public-defines.h和定義提取函數(shù)的tvbuf.h,tvbuf.c和定義異常處理函數(shù)的except.h,except.c。
為方便嵌入其他代碼模塊中,將整個(gè)RTPS協(xié)議解析代碼封裝為獨(dú)立模塊,僅通過(guò)如下幾個(gè)函數(shù)接口進(jìn)行調(diào)用:
模塊初始化接口,進(jìn)行協(xié)議解析模塊的內(nèi)存分配及初始化等,void rtps-init(void);
(1)
協(xié)議解析接口;gboolean dissect-rtps(tvbuff-t *tvb, packet-info *pinfo, proto-tree *tree, void *data);
(2)
應(yīng)用層數(shù)據(jù)解析回調(diào)函數(shù)設(shè)置接口,其中回調(diào)函數(shù)定義如(4)所示。
void set-rtps-dissector(dissect-rtps-func-ptr ptr);
(3)
typedef int (*dissect-rtps-func-ptr)(const char *topic, const char *data, int len, int *param);
(4)
使用時(shí)按照接口(2)規(guī)定的格式將獲取的RTPS網(wǎng)絡(luò)數(shù)據(jù)包輸入函數(shù)接口中就可以實(shí)現(xiàn)對(duì)RTPS協(xié)議的解析,剝離RTPS協(xié)議封裝數(shù)據(jù),最終應(yīng)用層數(shù)據(jù)將被傳入外部定義的應(yīng)用數(shù)據(jù)處理回調(diào)函數(shù)中,輸出的參數(shù)包括數(shù)據(jù)的主題號(hào)、應(yīng)用數(shù)據(jù)、長(zhǎng)度和自定義的用戶參數(shù),方便其他軟件模塊對(duì)應(yīng)用數(shù)據(jù)的解析分析。
為驗(yàn)證RTPS協(xié)議解析代碼剝離后的有效性,設(shè)計(jì)了一個(gè)簡(jiǎn)單的應(yīng)用數(shù)據(jù)協(xié)議分析軟件,嵌入剝離出的RTPS協(xié)議解析模塊,實(shí)現(xiàn)對(duì)使用DDS通信的應(yīng)用協(xié)議數(shù)據(jù)報(bào)文的解析,同時(shí)驗(yàn)證RTPS協(xié)議解析模塊的有效性。
軟件包含3個(gè)模塊,如圖2所示,其中網(wǎng)絡(luò)抓包模塊負(fù)責(zé)從網(wǎng)絡(luò)抓取數(shù)據(jù)包,采用Winpcap/libpcap的網(wǎng)絡(luò)抓包庫(kù)實(shí)現(xiàn),主要負(fù)責(zé)打開(kāi)網(wǎng)卡,并抓取網(wǎng)絡(luò)通信數(shù)據(jù)包,送入后續(xù)處理模塊;協(xié)議解析模塊負(fù)責(zé)將網(wǎng)絡(luò)數(shù)據(jù)預(yù)處理后,根據(jù)數(shù)據(jù)包的不同類型調(diào)用不同的解析模塊進(jìn)行協(xié)議的解析,上節(jié)中剝離出的RTPS協(xié)議解析功能代碼模塊將嵌入此模塊中,進(jìn)行RTPS協(xié)議的解析和協(xié)議封裝頭的去除;人機(jī)交互模塊主要負(fù)責(zé)將抓取的數(shù)據(jù)包以及解析結(jié)果顯示出來(lái)。下面重點(diǎn)介紹協(xié)議解析模塊的處理流程。
圖2 協(xié)議分析軟件模塊組成
協(xié)議解析模塊的處理流程如圖3所示。從網(wǎng)絡(luò)抓包模塊中接收網(wǎng)絡(luò)數(shù)據(jù)后,首先進(jìn)行網(wǎng)絡(luò)層協(xié)議解析,判斷是否需進(jìn)行IP組包。由于使用DDS通信時(shí),發(fā)送方底層會(huì)將待發(fā)送的長(zhǎng)RTPS協(xié)議數(shù)據(jù)報(bào)文或控制報(bào)文進(jìn)行IP層拆包發(fā)送,然后由接收方底層組包后再進(jìn)行解析。收包不全時(shí)無(wú)法解析出完整內(nèi)容,所以需對(duì)網(wǎng)絡(luò)層IP封裝頭部進(jìn)行解析,判斷是否需進(jìn)行組包處理。
組包完后再經(jīng)傳輸層協(xié)議解析判斷后,再判斷是否是RTPS協(xié)議包。對(duì)于是使用DDS通信的報(bào)文,則進(jìn)行RTPS協(xié)議解析,去除協(xié)議封包數(shù)據(jù)后,由應(yīng)用協(xié)議解析子模塊進(jìn)行應(yīng)用協(xié)議數(shù)據(jù)的解析,最后將解析結(jié)果輸出至下一個(gè)模塊。為支持非DDS通信應(yīng)用數(shù)據(jù)報(bào)文的解析,在判斷報(bào)文是非RTPS協(xié)議報(bào)后,直接將數(shù)據(jù)送入應(yīng)用協(xié)議解析子模塊中進(jìn)行判斷解析處理。
圖3 協(xié)議解析模塊處理流程
為進(jìn)行協(xié)議解析驗(yàn)證,假設(shè)使用DDS通信的序列化數(shù)據(jù)報(bào)文傳輸?shù)膽?yīng)用數(shù)據(jù)協(xié)議接口結(jié)構(gòu)定義如CGRAM所示,發(fā)送信息主題號(hào)為T(mén)EST-TOPIC。使用兩臺(tái)計(jì)算機(jī)點(diǎn)對(duì)點(diǎn)互通方式驗(yàn)證,發(fā)送信息內(nèi)容填充為發(fā)送方計(jì)算機(jī)日期和時(shí)間。
typedef struct tagCGRAM
{
unsigned short wTLen;
unsigned short wYear;
unsigned char ucMonth;
unsigned char ucDay;
unsigned char ucHour;
unsigned char ucMin;
unsigned char ucSec;
unsigned char ucMday;
char strName[100];
}CGRAM;
為做對(duì)比分析,同時(shí)使用Wireshark軟件和應(yīng)用數(shù)據(jù)協(xié)議分析軟件進(jìn)行抓包。選取一個(gè)完整DDS通信數(shù)據(jù)包,如圖4所示為Wireshark軟件中的數(shù)據(jù)包。Wireshark軟件會(huì)自動(dòng)識(shí)別DDS通信數(shù)據(jù)包并執(zhí)行RTPS協(xié)議解析,去除協(xié)議封裝之后的應(yīng)用數(shù)據(jù)如圖5所示。由于采用序列化傳輸方式,所以Wireshark軟件無(wú)法解析傳輸?shù)臄?shù)據(jù)是什么結(jié)構(gòu),各字段的值是多少。在同步運(yùn)行的應(yīng)用數(shù)據(jù)協(xié)議分析軟件中選擇同一個(gè)報(bào)文進(jìn)行應(yīng)用層解析后,如圖6所示給出了應(yīng)用數(shù)據(jù)中各字段的值??梢?jiàn)解析軟件中嵌入的RTPS協(xié)議解析模塊成功剝離了RTPS協(xié)議封裝數(shù)據(jù),給出了完整的應(yīng)用數(shù)據(jù),而應(yīng)用協(xié)議解析模塊也成功對(duì)應(yīng)用數(shù)據(jù)協(xié)議進(jìn)行了解析,給出了各字段的值。
圖4 Wireshark捕獲的RTPS數(shù)據(jù)包
圖5 Wireshark對(duì)RTPS數(shù)據(jù)包的解析
圖6 協(xié)議分析軟件對(duì)數(shù)據(jù)包的解析結(jié)果
本文針對(duì)使用DDS通信的應(yīng)用程序進(jìn)行調(diào)試、測(cè)試時(shí)應(yīng)用數(shù)據(jù)無(wú)法解析的問(wèn)題進(jìn)行了分析,并在分析開(kāi)源網(wǎng)絡(luò)封包分析軟件Wireshark工作原理的基礎(chǔ)上,研究實(shí)現(xiàn)了剝離其RTPS協(xié)議解析代碼的方法,最后將剝離出的協(xié)議解析代碼嵌入到設(shè)計(jì)的應(yīng)用協(xié)議分析軟件中,驗(yàn)證實(shí)現(xiàn)了對(duì)使用DDS通信的應(yīng)用協(xié)議數(shù)據(jù)的解析,為進(jìn)行設(shè)備信息互通和接口測(cè)試提供了工具,對(duì)實(shí)現(xiàn)類似基于開(kāi)源軟件的功能代碼移植有一定的參考意義。