簡體   English   中英

如何使用 NDIS 過濾器驅動程序讀取接收到的數據包?

[英]How can I read the received packets with a NDIS filter driver?

我目前正在試驗NDIS 驅動程序示例 我正在嘗試打印數據包內容(包括 MAC 地址、EtherType 和數據)。

我的第一個猜測是在 function FilterReceiveNetBufferLists中實現這個。 不幸的是,我不確定如何從NetBufferLists中提取數據包內容。

這是正確的起點。 考慮這段代碼:

void FilterReceiveNetBufferLists(..., NET_BUFFER_LIST *nblChain, ...)
{
    UCHAR buffer[14];
    UCHAR *header;

    for (NET_BUFFER_LIST *nbl = nblChain; nbl; nbl = nbl->Next) {
        header = NdisGetDataBuffer(nbl->FirstNetBuffer, sizeof(buffer), buffer, 1, 1);
        if (!header)
            continue;

        DbgPrint("MAC address: %02x-%02x-%02x-%02x-%02x-%02x\n",
            header[0], header[1], header[2],
            header[3], header[4], header[5]);
    }

    NdisFIndicateReceiveNetBufferLists(..., nblChain, ...);
}

關於這段代碼有幾點需要考慮。

NDIS 數據路徑使用 NET_BUFFER_LIST (nbl) 作為其主要數據結構。 一個 nbl 表示一組具有相同元數據的數據包。 對於接收路徑,沒有人真正了解元數據,因此該集合中始終只有 1 個數據包。 換句話說,nbl 是一個長度為 1 的列表。對於接收路徑,您可以指望它。

nbl 是一個或多個 NET_BUFFER (nb) 結構的列表。 nb 表示單個網絡幀(受 LSO 或 RSC 約束)。 因此,nb 最接近於您認為的數據包。 它的元數據存儲在包含它的 nbl 上。

在一個 nb 中,實際的數據包有效負載存儲為一個或多個緩沖區,每個緩沖區表示為一個 MDL。 從心理上講,您應該假裝 MDL 只是串聯在一起。 例如,網絡標頭可能在一個 MDL 中,而有效負載的 rest 可能在另一個 MDL 中。

最后,為了性能,NDIS 為您的 LWF 提供盡可能多的 NBL。 這意味着有一個或多個 NBL 的列表。

把它們放在一起,你有:

  • 您的 function 收到 NBL 列表。
  • 每個 NBL 恰好包含 1 個 NB(在接收路徑上)。
  • 每個 NB 都包含一個 MDL 列表。
  • 每個 MDL 指向一個有效負載緩沖區。

所以在我們上面的示例代碼中,for 循環沿着第一個要點進行迭代:NBL 鏈。 在循環中,我們只需要查看nbl->FirstNetBuffer ,因為我們可以安全地假設除了第一個 nb 之外沒有其他 nb。

必須直接擺弄所有這些 MDL 很不方便,因此我們使用輔助例程NdisGetDataBuffer 你告訴這個人你想查看多少字節的有效載荷,他會給你一個指向連續有效載荷范圍的指針。

  • 在好的情況下,您的緩沖區包含在單個 MDL 中,因此 NdisGetDataBuffer 只是為您提供指向該 MDL 緩沖區的指針。
  • 在較慢的情況下,您的緩沖區跨越多個 MDL,因此 NdisGetDataBuffer 會小心地將有效負載的相關位復制到您提供的暫存緩沖區中。

如果您嘗試檢查多個字節,則后一種情況可能很繁瑣。 如果您正在讀取數據包的所有 1500 字節,則不能只在堆棧上分配 1500 字節(內核堆棧空間稀缺,與用戶模式不同),因此您必須從池中分配它。 一旦弄清楚這一點,請注意,將所有 1500 字節的數據復制到每個數據包的暫存緩沖區中會減慢速度。 減速太多了嗎? 這取決於您的需求。 如果您只是偶爾檢查數據包,或者如果您將 LWF 部署在低吞吐量 NIC 上,則無關緊要。 如果你試圖超過 1Gbps,你不應該存儲這么多數據。

另請注意,如果您最終要修改數據包,則需要警惕 NdisGetDataBuffer。 它可以為您提供數據的副本(存儲在本地暫存緩沖區中),因此如果您修改有效負載,這些更改實際上不會粘在數據包上。

如果您確實需要擴展到高吞吐量或修改有效負載怎么辦? 然后你需要弄清楚如何操作 MDL 鏈。 起初這有點令人困惑,但花一點時間閱讀文檔並為自己畫一些白板圖。

我建議首先從了解 MDL 開始。 從網絡的角度來看,MDL 只是保存 { char * buffer, size_t length } 以及指向下一個 MDL 的鏈接的一種奇特方式。

接下來,考慮 NB 的 DataOffset 和 DataLength。 這些在概念上將緩沖區邊界從緩沖區的開頭和結尾移入。 他們並不真正關心 MDL 邊界——例如,您可以通過遞減 DataLength 來減少數據包有效負載的長度,如果這意味着一個或多個 MDL 不再為數據包有效負載提供任何緩沖區空間,那么它不是大不了,他們只是被忽略了。

最后,在頂部添加 CurrentMdl 和 CurrentMdlOffset。 這些對於上述所有內容都是多余的,但它們的存在是為了(微基准)性能。 如果您正在閱讀 NB,您甚至不需要考慮它們,但如果您正在編輯 NB 的大小,則需要更新它們。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM