<a href="http://kruglinski.bokee.com/5746261.html" target="_blank">http://kruglinski.bokee.com/5746261.html</a>
今天終于把NDIS Protocol Driver開發相關的東東看完了,現在回頭看其實協定驅動也不是很難,隻是有些概念沒有人點撥自己琢磨了很久,比如如何将收到的包寫入上層應用程式ReadFile讀取的buffer裡,我的腦海裡總有memcpy進行資料copy的概念,呵呵!
然而在NDIS協定驅動裡copy資料不是通過memcpy這樣的函數來完成的,這裡有一個NDIS_PACKET的概念,當接收資料時先為NDIS_PACKET關聯一個記憶體描述符NDIS_BUFFER(其實就是MDL),使用NdisChainBufferAtFront關聯Buffer和Packet再調用NdisTransferData接收剩下的資料,ReceiveHandler和ReceivePacketHandler的處理方法是不一樣的,ReceiveHandler先接收一個以太頭然後按照我上面說的方法接收剩下的資料,因為要将頭和餘下資料都寫入同一個塊記憶體可能除了IRP裡的MDL,還需要建立一個對同一個記憶體塊不同位置的子描述符MDL.
而在ReceivePacketHandler裡沒有将以太頭和資料包分開處理而是直接放在一個NDIS_PACKET裡,是以隻需要連接配接一個NDIS_BUFFER,然後使用NdisCopyFromPacketToPacket直接将資料copy進去即可.
在Windows 2000 DDK例子驅動Packet的PacketReceiveIndicate裡可以看到先使用NdisMoveMappedMemory将以太頭寫入輸出MDL指向的記憶體裡,然後建立相同記憶體塊的另一個子MDL跳過以太頭,随後調用NdisChainBufferAtFront連接配接buffer到packet,最後調用NdisTransferData接收資料,并在TransferDataCompleteHandler将建立的子MDL删除,再将PacketRead時配置設定的NDIS_PACKET釋放,并完成挂起的讀請求IRP.
發送的概念很容易了解,主要就是調用NdisSend将資料從某個綁定的擴充卡(Adapter)發送出去,同樣因為是異步的是以要處理SendCompleteHandler,主要就是将PacketWrite時配置設定的NDIS_PACKET包釋放并完成挂起的寫請求IRP.
而嗅探最常用的混雜模式是通過調用NdisRequest的發送NdisRequestSetInformation類型的OID_GEN_CURRENT_PACKET_FILTER請求,并使用NDIS_PACKET_TYPE_PROMISCUOUS參數.
主要就是要把大的流程把握清楚,其它細節的東西可以說的太多了.我覺得主要是由協定驅動設計所決定,而跟NDIS協定驅動本身的架構不是很相關,這裡不再多說.
我覺得Windows 2000 DDK帶的協定驅動例子是一個非常糟糕的例子,幾乎沒有可以分開解讀的地方,必須一起讀好幾個函數,在某個函裡會跟到一個NdisWaitEvent,然後又跳到另一個函數裡看相關實作是怎麼将事件設定成信号态的,某一個函數裡引用一個全局變量的值,然後又要搜尋在哪個函數裡設定了這個全局變量值,怎麼設定的,為什麼那樣設定,中間有太多的藕合,如控制裝置和擴充卡邏輯裝置使用同一組派遣函數(當然一個驅動對象也隻能有一組,但在DriverStudio裡經過包裝後的驅動架構每個裝置對象的派遣函數是不一樣的,它們存在于不同的C++對象中),說到這裡我一定要說一下DriverStudio裡帶的DriverNetworks有一個完全按照DDK Packet例子實作的驅動程式叫做nmPacket,在DriverNetworks的協定驅動架構裡,分成四個C++對象,驅動對象,控制裝置對象,擴充卡邏輯裝置對象和協定綁定對象.
驅動對象裡建立全局控制裝置對象并注冊協定綁定對象.協定綁定對象是對協定驅動的抽象,它的重載成員函數就是協定導出函數,在協定綁定對象的打開擴充卡慮函數Open裡,建立一個擴充卡邏輯裝置對象并傳this指針.
協定綁定對象有一個靜态連結清單成員,每次在擴充卡打開後(OpenComplete被調用時)加入一個條目,表示已經綁定的擴充卡,全局控制裝置對象可以通過讀這個成員取得綁定清單并傳回給應用程式.擴充卡邏輯裝置對象因為有協定綁定對象指針,可以随時調用協定驅動對象的相關功能(NdisSend,NdisRequest...),如在裝置的Read派遣函數裡在協定驅動對象的讀連結清單裡添加一個條目,随後協定綁定對象的接收函數裡會從這個連結清單裡取讀取求,然後在在分割以太頭和一次接收整個NDIS_PACKET的兩個協定導出函數中接收資料,為讀請求寫入資料,并完成一個讀操作.
DriverNetworks實作的這個協定驅動程式的面向對象抽象,功能劃分與子產品間解藕合做的非常非常好,代碼可讀性也非常好,編譯好的驅動程式隻比DDK的Packet隻大一點點.我不是很清楚這麼優秀的東東怎麼沒有流行起來,也或許已經非常流行隻是我周圍沒有人用罷了,反正我是決定要把它用好,呵呵!
哈!在家的感覺真好......