• 沒有找到結果。

網路中介層驅動程式是實作

第四章 移植RIOMIP

4.3 替代方案的設計與實作

4.3.2 網路中介層驅動程式是實作

在 2-2 中我曾經介紹過 NDIS 概念。其中有簡介了中介層驅動程式的概念和功能。

藉由他能夠攔截封包的能力,我將實作一個中介層驅動程式來代替原來的 NdisFlt 驅動 程式。在這節當中,我將詳細敘述如何實作一個網路中介層驅動程式,和如何利用它來 做攔截封包的功能。因為功能的需求,我在這邊時作的是過濾型的中介層驅動程式。

要實作一個網路中介層驅動程式,必須要實作兩個介面:一個是協定驅動程式介 面,一個是迷你連接埠驅動程式介面。前者就是所謂的 ProtocolXxx 函式,後者就是 MiniportXxx 函式。因為對上層的協定驅動程式來說,中介層驅動程式就好像迷你連接 步趨動程式;而對下層的迷你連接埠驅動程式而言,中介層驅動程式則是扮演協定層驅 動程式的角色。藉由這兩種介面,中介層驅動程式才能利用 NDIS 函式庫,和上下層的 驅動程式互動。而這兩種介面,所需要實做的函式如表 4-2 所示,底下我就幾個重要的 函式做詳細的說明。

表 4-2NDIS 中介層驅動程式介面函式

z 協定驅動程式介面 ProtocolBindAdapter

當每一個網路介面卡被迷你連接埠初始化後,NDIS 都會主動來呼叫這個函 式,來要求協定驅動程式去跟底層的迷你連接埠驅動程式做連結。而在這個函式

43

裡面,最重要的是就是去呼叫 NdisOpenAdapter 去連結底層的網路界面裝置。然 而,因為要實做中介層驅動程式,在這個函式裡必須去產生虛擬連接埠裝置。而 透過呼叫 NdisIMInitializeDeviceInstanceEx 函式,我們可以產生一個虛擬連 結埠。

而在過濾型的中介層驅動程式中,虛擬連接埠裝置根底層的實體網路介面卡 的對應關係是一比一的,所以對於每個實體的網路介面卡,都必須產生一個虛擬 連接埠裝置。也就是這個函式每被呼叫一次,就必須產生一個虛擬連接裝置,這 和多路傳輸中介層驅動程式很不一樣的。

ProtocolRecievePacket

當底層的迷你連結埠驅動程式收到封包之後,它通常會呼叫

NdisMIndicateReceivePacket,而NDIS就會去呼叫與他有連結的協定驅動程式本 處理函式,來做接收封包的動作。而在中介層驅動程式裡,在處理完封包之後,

如果有需要,我們則必須再去呼叫NdisMIndicateReceivePacket,繼續把封包往 上傳遞給上層的協定驅動程式。

ProtocolStatus

底層的迷你連接埠驅動程式通常會維護一些網路介面卡的狀態,當狀態有所 改變時,它會去呼叫 NdisMIndicateStatus 來告知上層的協定驅動程式。而協定 驅動程式就必須實做本處理函式,來接受這些狀態的改變。而在中介層驅動程式 當中,我們通常會再次呼叫 NdisMIndicateStatus,來告知上層的協定驅動程式 這些狀態的改變。

ProtocolSendComplete

由於網路傳輸的特性,為了得到較好的效能和降低中央處理器所浪費的時 間,NDIS 支援上次的協定驅動程式和下層的迷你連接埠驅動程式之間的相互操作 是可以非同步的。已傳送封包為例,在協定驅動程式呼叫了 NdisSend 之後,NDIS 函式庫會去呼叫底層迷你連接埠所實作的 MiniportSend 處理函式。但是,在 MiniportSend 處理函式中,迷你連接埠並不一定要馬上去傳送封包,因為某些效 能考量和底層的網路介面卡的特性,MiniportSend 處理函式可以先回應

STATUS_PENDING,代表封包正在等待處理中,之後就返回。而剛剛協定驅動程式 呼叫的 NdisSend 就會收到 STATUS_PENDING 的傳回執。而等到迷你連接埠真正的 把封包透過網路介面卡傳送出去之後,它會去呼叫 NdisSendComplete 來告知協

44

定驅動程式剛剛的封包已經傳送出去。NDIS 函式庫就去呼叫協定驅動程式的 ProtocolSendComplete 處理函式,來做後續的動作。整個過程如圖 4-15 所示。

通常我們會在這邊做資源釋放的動作,包括存放封包所用的緩衝區和封包描述子 等。關於這些資源的詳細作用,我會在稍後討論到封包處理的部份,進一步的詳 細說明

圖 4-15NDIS 封包傳送非同步運作模式

z 迷你連接埠驅動程式介面 MiniportInitialize

在中介層驅動程式中,當協定驅動程式介面中的 Protocol- BindAdapter 函式 被呼叫時,它會去呼叫 NdisIMInitialize- DeviceInstanceEx 函式來產生虛擬連 接埠,然而 NDIS 函式庫就會呼叫本處理函式來作對虛擬函式庫作出初始話的動作。

MiniportQueryInformation

本處理函示負責接收由上層的協定驅動程式呼叫 NdisRequest 來傳送類別為 NdisRequestQueryInformation 的 OID_XXX 請求。這些請求是為了查詢下層迷你連 接埠所維護的網路介面卡的狀況和網路參數設定。 除了一些特殊的情況,在中介

45

層驅動程式中,我們同常會在次呼叫 NdisRequest 函式把請求傳遞給下面的虛擬連 接埠驅動程式。

MiniportSetInformationHandler

相對於 MiniportQueryInformation,這個函式接收的 OID_XXX 請求類別為 NdisRequestSetInformation,是用來設定下層迷你連接埠所維護的網路介面卡的 參數。

MiniportSendPacket

在這個函式中,我們會接受到上層協定驅動程式所送下來的封包陣列。在這個 函式中,我們需要重新包裝 (repackage) 封包,作配置新的封包描述子 (packet descriptor),串連封包的緩衝區等動作。最後在處理完封包之後,呼叫 NdisSend 或 Ndis- SendPackets 把封包傳送給底層的迷你連接埠驅動程式 。

進一步,我將介紹如何用過濾型中介層驅動程式來實作攔截封包的功能。首先,我 將解說在 NDIS 驅動程式中封包是如何的被描述和儲存。

NDIS 封包結構主要是由兩種資料結構來表示:

z NDIS_PACKET: 所謂的封包描述子,用來描述在 NDIS 驅動程式裡面的一個封 包,包含一指標來串連存放封包資料的緩衝區。底下則是 NDIS_PACKET 的完整資料 結構。其中 Private 欄位唯一指標,用來串接用 NDIS_BUFFER 資料結構所表示的緩 衝區;而 MiniportReserved 和 ProtocolReserved 為兩個保留欄位,在我們的中介 層驅動程式當中,我們用這兩個欄位來存放指向上層協定驅動程式傳下來和底層迷 你連接埠驅動程式傳上來的封包描述子的指標。資料結構如圖 4-16

46

圖 4-16NDISPACKET資料結構

z NDIS_BUFFER: 用來存放封包的緩衝區,在不同的平台,這個資料結構的型態 都不一樣。在 Windows CE 作業系統中,NDIS_BUFFER 為一 MDL(Memory Descriptor List),用來描述一段在虛擬記憶體空間連續的記憶體。

圖 4-17 和圖 418x 圖示上面兩種資料結構如何描述一個封包。須要注意的是,NDIS 並沒有規定一個封包需要用幾個 NDIS_BUFFER 來串連表示。

47

圖 4-17SINGLE-BUFFER NDIS_PACKET 示意圖

圖 4-18 MULTI-BUFFER NDIS_PACKET 示意圖

而在驅動程式裡面,我們通常並不直接存取這些資料結構的欄位,而是透過 NDIS 所提供的函式來存取封包。主要常用的函式和其功用如表 4-3 所示。

48

NdisQueryPacket Returns information about a given packet descriptor

BufferCount Count of buffer descriptors chained to the packet descriptor

FirstBuffer pointer to the initial buffer descriptor chained to the given packet descriptor

TotalPacketLength The total number of bytes of packet data mapped by all chained buffer descriptors NdisQueryBuffer Returns information about a given buffer

descriptor

VirtualAddress Base virtual address of the virtual address range described by the buffer descriptor

Length Number of bytes in the virtual address range described by the buffer descriptor.

NdisGetNextBuffer Returns the next buffer descriptor in a chain, given a pointer to the current buffer descriptor

表 4-3NDIS 存取封包相關函式

在中介層驅動程式,不管是從上層協定驅動程式傳遞下來的封包,或者是由下層迷 你連接埠驅動程式所上傳的封包,會是由 NDIS_PACKET 封包描述子所表示的。所以在程 式中,我們必須藉由表 4-3 中的函式,才能存取到封包的內容。一般的步驟如下所示:

1. 呼叫 NdisQueryPacket 函式,我們可以得到封包描述子所串接的第一個 NDIS_BUFFER 緩衝區描述子。

2. 呼叫 NdisQueryBuffer 函式,可以取得步驟 1 所取 NDIS_BUFFE 緩衝區描述子 的緩衝區的虛擬記憶體位址,即是一個指向封包資料的指標。

3. 因為每一個 NDIS_PACKET 封包描述子所串接的緩衝區可能不只一個,所以我們 可以呼叫 NdisGetNextBuffer 來取得下一個 NDIS_BUFFE 緩衝區描述子,重複步驟 2,直到沒有傳接的緩衝區,就可以取得一個完整的封包內容。

底下圖 4-19 我實作的中介層驅動程式的片段程式碼,展示了如何取得的一個完整 的封包內容。

49

然而,要做封包攔截的動作,其實就是在封包通過的路徑上,插入我們自己的過慮 函式,透過上述的方法,取得封包的完整資料,檢查封包的內容,看這個封包是不是我 們所要攔截的。如果是,就把封包的完整資料拷貝到特定的緩衝區,最後在利用前 4.3.1 節所敘述非同步輸入輸出的方法,把封包傳送給我們上層的應用程式模組。

而由之前所介紹的 NDIS 驅動程式界面,包括了迷你連接埠的介面和協定的介面,

可以看出來外送和內收的封包都分別會經過 MiniportSendPackets 和 ProtocolRecievePackets 這兩個處理函式。

所以我們所要作的,就是把我們設計的過濾函式加入這兩個函式當中,把所要攔截 的封包攔截下來;而對於其他的封包,則讓他繼續傳送給下層的迷你連接埠驅動程式或 者是上層的協定驅動程式。

50

圖 4-19 取的封包內容範例

51

另外有一點很重要的是,因為不管從上層的協定驅動程式,或者是下層的迷你連接 埠驅動程式所傳遞過來的封包描述子,都是屬於它們本身的。當我們要將封包繼續往上 或往下傳送時,我們必須做一個重新包裝的動作。因為在封包描述子中有包含一些屬於 原本驅動程式特別紀錄的資訊 (MiniportReserved 和 ProtocolReserved)。我們必須 重新配置至一個新的封包描述子,將原本封包描述子的一些封包資訊複製一份到新的封 包描述子。如果整個封包的內容並未作改變,你可以將新的封包描述子緩衝區的指標,

指向原來封包描述子所指向的緩衝區描述子。但是由於原來封包描述子所指向的緩衝區 描述子本身也是屬於原來的驅動程式的,所以我們對緩衝區描述子及所描述的緩衝區內 容並不能任意更改。我們只能把它當作是唯讀的。如果我們要改變封包的內容,我們也 比須配置屬於我們自己的緩衝區,和緩衝區描述子。

指向原來封包描述子所指向的緩衝區描述子。但是由於原來封包描述子所指向的緩衝區 描述子本身也是屬於原來的驅動程式的,所以我們對緩衝區描述子及所描述的緩衝區內 容並不能任意更改。我們只能把它當作是唯讀的。如果我們要改變封包的內容,我們也 比須配置屬於我們自己的緩衝區,和緩衝區描述子。

相關文件