Fang
驱动牛犊
驱动牛犊
  • 注册日期2001-06-12
  • 最后登录2010-08-10
  • 粉丝0
  • 关注0
  • 积分2分
  • 威望13点
  • 贡献值0点
  • 好评度2点
  • 原创分0分
  • 专家分0分
阅读:2880回复:3

转贴:NDIS "Packet" Discussion

楼主#
更多 发布于:2001-08-14 10:52


请各位自己去看:
http://pcausa.com/resources/ndispacket.htm

 
 

  NDIS "Packet" Discussion
The purpose of this topic is to provide you with some insight into the relationship between a "packet" observed a network and a NDIS_PACKET that represents the same information within a NDIS driver.

 





Packet Data On The Network
The first step is to actually understand what is observed on the network. The following is a HEX dump of a simple ICMP "ping" packet:

000000: 00 A0 CC 63 08 1B 00 40 : 95 49 03 5F 08 00 45 00 ...c...@.I._..E.
000010: 00 3C 82 47 00 00 20 01 : 94 C9 C0 A8 01 20 C0 A8 .<.G.. ...... ..
000020: 01 40 08 00 48 5C 01 00 : 04 00 61 62 63 64 65 66 .@..H\....abcdef
000030: 67 68 69 6A 6B 6C 6D 6E : 6F 70 71 72 73 74 75 76 ghijklmnopqrstuv
000040: 77 61 62 63 64 65 66 67 : 68 69                   wabcdefghi......

The ping was initiated with the command:

C:> ping 192.168.1.64
and sent the ICMP echo request with the default of 32 bytes of data. The total length of the ping packet is 74 bytes.

The packet was captured using the PCAUSA Rawether for Windows HookPeek sample application. HookPeek isn't a network monitor by a long shot, but it has its uses...



A partial decode of this packet is provided for reference.

[ Click here to view the partial packet decode ]

 





NDIS Representation Of Packet Data
The information of interest is, of course, the packet data. Somewhere there are 74 bytes of virtual memory that represent the 74 bytes of data that is observed on the network. At first the mechanism used by NDIS to manage packet data may seem unnecessarily complicated. However, the basic mechanism is well thought out and provides a great deal of flexibility to a protocol writer.

 

Simple NDIS_PACKET Representation
In general it is best to think of the NDIS_PACKET structure (as well as the associated NDIS_BUFFER structures) as "opaque" - except for specific "reserved" fields that are discussed later. This means that the fields that are defined in the structure should not be directly accessed and that their definition will probably change from one NDIS version to another.

Nevertheless, it is useful to have a visual idea of these structures and how they relate to the packet data.

The diagram below illustrates the simplest way that an NDIS_PACKET can be used to encapsulate packet data:

 



Figure 1. Simple NDIS_PACKET Illustration

 

In this simple case, all 74 bytes of packet data are located in a contiguous array of 74 bytes.

Here is a description of this NDIS_PACKET:

The NDIS_PACKET has one chained NDIS_BUFFER. The NDIS_BUFFER describes a 74-byte range of virtual memory that contains the complete packet data.

 

A More Complex NDIS_PACKET Representation
The diagram below illustrates another way that a NDIS_PACKET can encapsulate the same packet data.

 



Figure 2. Multi-Buffer NDIS_PACKET Illustration

 

In this case packet data is found in two disjoint ranges of virtual memory.

Here is a description of the second NDIS_PACKET:

The NDIS_PACKET has two chained NDIS_BUFFER. The first NDIS_BUFFER describes a 14-byte range of virtual memory that contains the Ethernet header. The second NDIS_BUFFER describes a 60-byte range of virtual memory that contains the Ethernet payload.

It should start to become apparent that there are no safe assumptions on how a NDIS_PACKET will be constructed. The actual construction is at the sole discretion of the software that built the packet in the first place.

 





Interpreting A NDIS_PACKET
Although the diagrams above provide a helpful picture of how an NDIS_PACKET is used, you must recall that these structures are "opaque". You should never access the fields of these structures directly. Instead, you should use access functions provided by the NDIS wrapper library.

In the following topics we'll discuss a few of the NDIS wrapper library functions. The specific focus will be on the minimum functions that you need to examine and interpret packet data if you are given an NDIS_PACKET to inspect.

There are only a few NDIS functions that you need to inspect a NDIS_PACKET and the NDIS_BUFFERs that are chained to it:

 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.
  

NDIS 5.1 Note: NDIS 5.1 ("Whistler") introduces new "safe" versions of several NDIS functions. The "safe" versions should be used for NDIS 5.1 drivers:

 NdisQueryBufferSafe - The safe version of NdisQueryBuffer.

Here is a code snippet that uses NdisQueryPacket to examine the NDIS_PACKET pointed to by pNdisPacket:

   PNDIS_BUFFER    pCurrentBuffer;
   UINT            nBufferCount, TotalPacketLength;

   //
   // Query Packet
   //
   NdisQueryPacket(
      (PNDIS_PACKET )pNdisPacket,
      (PUINT )NULL,           // Physical Buffer Count
      (PUINT )&nBufferCount,  // Buffer Count
      &pCurrentBuffer,        // First Buffer
      &TotalPacketLength      // TotalPacketLength
      );
The code snippet fetches the count of NDIS_BUFFERs chained to the packet and the total length of the bytes of packet data mapped by all buffer descriptors chained to the packet. It also fetched a pointer to the first NDIS_BUFFER chained to the packet.

 

The NdisQueryBuffer function can be used to extract information from a NDIS_BUFFER, as illustrated below:

   PUCHAR    VirtualAddress;
   UINT      CurrentLength, CurrentOffset;
   //
   // Query The First Buffer
   //
   NdisQueryBuffer(
      CurrentBuffer,
      &VirtualAddress,
      &CurrentLength
      );
The snippet fetches the virtual address and length of the virtual memory described by the buffer descriptor.

We can manipulate the memory pointed to by VirtualAddress as an ordinary array of unsigned characters.

Understand, however, that all the packet data may not be described by the first buffer descriptor. If the NDIS_PACKET was the simple one illustrated in Figure 1 above, then CurrentLength would be 74 and all of the packet data would actually be at VirtualAddress.

If pNdisPacket pointed to the packet illustrated in Figure 2 above, then the situation would be different. The TotalPacketLength returned from NdisQueryPacket would be 74; however, the CurrentLength returned from querying the first buffer descriptor using NdisQueryBuffer would be 14.

This means that we would have to examine other buffers chained to the packet descriptor to see the rest of the packet data. At this point one would use NdisGetNextBuffer to fetch the next buffer descriptor, etc. This iterative process (sometimes called "walking the buffer chain") would continue as necessary to examine the packet data.

The UTIL_ReadOnPacket function illustrates the technique of "walking the buffer chain" to "read" a byte range on an arbitrary packet descriptor. The function is used by PCAUSA to safely "peek" into packet descriptors of unknown construction. It may not be the optimum function to use in all situations; for example, if one intended to copy larger regions of a network packet (e.g., IP data on network with large MTU), then a more efficient function would be in order.

[ Click here for UTILReadOnPacket code snippet... ]

 





Constructing Your Own NDIS_PACKET


 



Building A NDIS_PACKET For NdisMIndicateReceive
 There is a bug in the TCP/IP stack. The TCP/IP ReceivePacket handler does not support multiple NDIS_BUFFERs (MDLs) if the virtual memory for what would be the LookaheadBuffer spans multiple NDIS_BUFFERs.

The best approach to accomodate this fix is to make sure that the first buffer (MDL) in the packet you indicate using NdisMIndicateReceive has at least header size  plus lookahead size (OID_GEN_CURRENT_LOOKAHEAD) bytes of data.

This fix will work on Windows NT (since the TCP/IP driver does not have a ReceivePacket handler) as well as Windows 2000 and Whistler. It is planned to be fixed eventually, perhaps as early as BlackComb - the follow-on to Whistler.

 





NDIS_PACKET Reserved Areas
The NDIS_PACKET structure includes several "reserved" members that can be used by NDIS drivers to save private information in a packet descriptor. The two most useful fields are:

 MiniportReserved - An eight-byte area that "miniports" can use for their own purposes.
 ProtocolReserved - A variable-sized area (at least 16 bytes in length) that "protocols" can use for their own purposes.

Use of these fields can significantly simplify the management of packet descriptors. The fields can be used to save private information about a packet descriptor or be used to queue a packet descriptor in a linked list.

There is one additional reserved area of interest:

 WrapperReserved - An eight-byte area reserved exclusively for use by the NDIS wrapper.

Although use of the WrapperReserved area is propritary to the NDIS wrapper, it is probable that it is used by NDIS to keep information specific to the binding on which a packet is sent or received.

The requirement that "NDIS Intermediate drivers must repackage each incoming packet in a fresh packet descriptor before it passes the transfer request down to an underlying driver or up to a higher-level driver" is actually to insure that a unique WrapperReserved area is available for the NDIS wrapper; it actually doesn't matter whether the MiniportReserved or ProtocolReserved areas are used.

 Neither Miniports nor Protocols should alter the contents of the WrapperReserved area.

 

Basic Rules For Using NDIS_PACKET Reserved Members
The basic rules for "ownership" of NDIS_PACKET reserved members are simple and consistent.

If a protocol driver allocates a packet descriptor (to be sent "down the stack" for transmission), then:

The protocol driver can use the ProtocolReserved area.
The lower-level miniport driver can use the MiniportReserved area.
If a miniport driver allocates a packet descriptor (to be indicated "up the stack" for reception), then:

The miniport driver can use the MiniportReserved area.
The higher-level protocol driver can use the ProtocolReserved area.
 

Use Of NDIS_PACKET Reserved Members In NDIS IM Drivers
The introduction of NDIS Intermediate (IM) drivers complicates the use of the NDIS_PACKET reserved fields somewhat. A NDIS IM driver includes both upper-edge miniport functionality and lower-edge protocol functionality.

Actually, the rules remain the same if you look at each "edge" of a NDIS IM individually.

There are four cases that must be considered in understanding the use of NDIS_PACKET reserved fields in an NDIS IM driver:

 Packets Being Sent

Packets Entering The NDIS IM Driver At The Upper Edge (Miniport)
Packets Leaving The NDIS IM Driver At The Lower Edge (Protocol)
 Packets Being Received

Packets Entering The NDIS IM Driver At The Lower Edge (Protocol)
Packets Leaving The NDIS IM Driver At The Upper Edge (Miniport)
 

Packets Entering The NDIS IM Driver At The "Upper Edge"
(Handling Packets Being Sent To Your NDIS IM Miniport Edge)
The "upper-edge" of a NDIS IM driver looks like a miniport driver. Your NDIS IM driver is functioning as a miniport driver when the SendHandler or SendPacketsHandler functions are called.

Within these "miniport" functions it is easy to understand that:

The higher-level protocol driver that called your NDIS IM driver owns the ProtocolReserved area of each packet descriptor.
Your NDIS IM driver miniport functionality owns the MiniportReserved area of each packet descriptor that was passed to you.
The essential point to understand at this critical point is that the "ownership" of both of these reserved fields is already established.

Even if you don't elect to use the MiniportReserved area in the packet descriptor that was given to you, the miniport edge of your NDIS IM driver owns it - and it can't be "given away". Similarly, the higher-level protocol "owns" the ProtocolReserved area of the packet descriptor - and you can't use it.

Since your miniport edge "owns" the MiniportReserved area of the packet descriptor, it can use it for whatever purpose you wish. You could, for example, use it to queue the packet being sent for deferred processing.

 

Packets Leaving The NDIS IM At The "Lower Edge"
(Handling Packets Being Sent By Your NDIS IM Protocol Edge)
The "lower-edge" of a NDIS IM driver looks like a protocol driver. Your NDIS IM driver is functioning as a protocol driver when it is calls at the NdisSend or NdisSendPackets.

A protocol must allocate a new packet descriptor in order to call NdisSend or NdisSendPackets.

 Concerning the new packet descriptor allocated by your NDIS IM protocol edge:

The NDIS IM protocol edge can use the ProtocolReserved area.
The lower-level miniport driver can use the MiniportReserved area.
 

At this point you must fill your NDIS_PACKET with packet data to be sent.

There are two basic cases:

Spontaneous Packet Send (Completely New Packet)

In this case your NDIS IM behaves exactly as an ordinary NDIS protocol driver. Virtual memory is allocated and filled with packet data, a NDIS_BUFFER is allocated to describe the packet data virtual memory and the completely new NDIS_PACKET is sent.

 

Passthru Of Packet From Higher-Level Protocol (Re-Wraping A Packet)

We have already allocated the required new NDIS_PACKET.

In this case we can simply "re-wrap" the packet that was passed to us from the higher-level protocol in the "fresh" packet descriptor. We do this by setting the Private.Head and Private.Tail members of our fresh packet descriptor to the values of the packet descriptor that was passed to us from above.

 The "re-wrapping" techniques allows the NDIS IM to pass the original packet data on down to be sent without requiring it to be copied. At the same time, the "fresh" packet descriptor provides new and separate NDIS_PACKET reserved fields that are required to support the send operation.

 

Packets Entering The NDIS IM Driver At The "Lower Edge"
(Handling Packets Being Received At Your NDIS IM Protocol Edge)
The "lower-edge" of a NDIS IM driver looks like a protocol driver. Your NDIS IM driver is functioning as a protocol driver when it is called at the ReceiveHandler or ReceivePacketHandler are called.

Within these "protocol" functions it is easy to understand that:

The lower-level miniport driver that called your NDIS IM driver owns the MiniportReserved area of each packet descriptor.
Your NDIS IM driver protocol functionality owns the ProtocolReserved area of each packet descriptor that was passed to you.
The essential point to understand at this critical point is that the "ownership" of both of these reserved fields is already established.

Even if you don't elect to use the ProtocolReserved area in the packet descriptor that was given to you, the protocol edge of your NDIS IM driver owns it - and it can't be "given away". Similarly, the lower-level miniport "owns" the MiniportReserved area of the packet descriptor - and you can't use it.

Since your protocol edge "owns" the ProtocolReserved area of the packet descriptor, it can use it for whatever purpose you wish. You could, for example, use it to queue the packet being received for deferred processing.

 

Packets Leaving The NDIS IM At The "Upper Edge"
(Handling Packets That Your NDIS IM Protocol Edge Indicates Upwards As Received Packets)
The "upper-edge" of a NDIS IM driver looks like a miniport driver. Your NDIS IM driver is functioning as a miniport driver when it calls NdisMIndicateReceivePacket.

A miniport must allocate a new packet descriptor in order to call NdisMIndicatReceivePacket.

 Concerning the new packet descriptor allocated by your NDIS IM miniport edge:

The NDIS IM miniport edge can use the MiniportReserved area.
The higher-level protocol driver can use the ProtocolReserved area.
 

At this point you must fill your NDIS_PACKET with packet data to be indicated upwards as if it was received from the network.

There are two basic cases:

Spontaneous Packet Receive (Completely New Packet)

In this case your NDIS IM behaves exactly as an ordinary NDIS miniport driver. Virtual memory is allocated and filled with packet data, a NDIS_BUFFER is allocated to describe the packet data virtual memory and the completely new NDIS_PACKET is indicated upwards by calling NdisMIndicateReceivePacket.

 

Passthru Of Packet From Lower-Level Miniport (Re-Wraping A Packet)

We have already allocated the required new NDIS_PACKET.

In this case we can simply "re-wrap" the packet that was passed to us from the lower-level miniport in the "fresh" packet descriptor. We do this by setting the Private.Head and Private.Tail members of our fresh packet descriptor to the values of the packet descriptor that was passed to us from below.

 The "re-wrapping" techniques allows the NDIS IM to pass the original packet data on upwards to appear as received without requiring it to be copied. At the same time, the "fresh" packet descriptor provides new and separate NDIS_PACKET reserved fields that are required to support the receive operation.

 





Other Considerations
Here are some additional points to consider:

 
Don't Modify Objects That You Don't Own
 Do not modify packet data virtual memory that you did not allocate your self.

 Do not modify or adjust a buffer descriptor that you did not allocate your self.

 Do not modify or adjust a packet descriptor that you did not allocate your self (except for the ProtocoReserved of MiniportReserved areas under specific situations).

If you are writing a filter driver that encrypts or compresses data, then you must must develop a method that does not modify or alter the originals. Generally, this means that you must either make your own copy of the data to be encrypted of compressed or perform these operations "on-the-fly".

NDIS 5.1 Note: NDIS 5.1 ("Whistler") introduces new functionality called "packet stacking". This is mechanism that allows a NDIS IM driver to pass a packet to an adjacent driver without having to allocate a "fresh" packet descriptor.

 

A NDIS_BUFFER Is A Memory Descriptor List (MDL)
Well, at least this statement is true on Windows NT and Windows 2000.

A MDL is a fairly complex structure that describes pages in a virtual buffer in terms of physical pages. One can gain quite a bit of insight into the need for buffer descriptors by reading and understanding about Windows NT memory architecture.

On Windows 9X a NDIS_BUFFER is similar to a Windows NT MDL. If one consistently uses the NDIS wrapper functions to manipulate NDIS_BUFFER structures then the cross-platform differences are effectively hidden.

On all Windows platforms the NDIS_BUFFER is essential to management of memory that eventually touches hardware devices such as NICs.

The use of this sort of structure at this level of device driver programming is not at all unique to Windows. On Unix there is the "mbuf" structure and on the Macintosh there is the "BDS" structure. If you are working on a virtual memory host, then there will be a structure similar to a MDL (a.k.a NDIS_BUFFER) used for a similar purpose.

On Windows NT and Windows 2000 a highest-level NDIS driver can actually use MDL and NDIS_BUFFER interchangeably. In fact, you will see DDK samples where a MDL passed to a NDIS protocol driver (e.g., "packet" driver or "ndisuio" driver samples) is simply chained to a NDIS_PACKET using NdisChainBufferAtFront.

 Only highest-level Windows NT protocols can use MDLs set up by still higher-level drivers as substitutes for NDIS_BUFFER-type descriptors.

 

 
 
 


PCAUSA Home ?/font> Privacy Statement ?/font> Products ?/font> Ordering ?Support ?/font> Utilities ?/font> Resources
Mailing Lists  ?PCAUSA Newsletter ?PCAUSA Discussion List
Rawether for Windows and WinDis 32 are trademarks of Printing Communications Assoc., Inc. (PCAUSA)
Microsoft, MS, Windows, Windows 95, Windows 98, Windows Millennium, Windows 2000, and Win32 are registered trademarks and Visual C++ and Windows NT are trademarks of the Microsoft Corporation.
Send mail to webmaster@pcausa.com with questions or comments about this web site.
Copyright ?1996-2001 Printing Communications Assoc., Inc. (PCAUSA)
Last modified: July 04, 2001

最新喜欢:

txysptxysp sourensouren
moqingsong
论坛版主
论坛版主
  • 注册日期2002-04-07
  • 最后登录2011-02-03
  • 粉丝0
  • 关注0
  • 积分74分
  • 威望71点
  • 贡献值0点
  • 好评度10点
  • 原创分0分
  • 专家分0分
沙发#
发布于:2002-05-15 20:52
这文章不错。
按第一贴的“给分”键,给分。
fenger_li
驱动老牛
驱动老牛
  • 注册日期2002-03-26
  • 最后登录2005-04-10
  • 粉丝0
  • 关注0
  • 积分0分
  • 威望0点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
板凳#
发布于:2002-05-15 21:25
确实不错,我看了好几遍。呵呵!

以后希望大家多贴些这样的东西
有点意思。。。 呵呵!
moqingsong
论坛版主
论坛版主
  • 注册日期2002-04-07
  • 最后登录2011-02-03
  • 粉丝0
  • 关注0
  • 积分74分
  • 威望71点
  • 贡献值0点
  • 好评度10点
  • 原创分0分
  • 专家分0分
地板#
发布于:2002-05-16 09:09
to fenger_li
不知你对ndis的数据结构 NDIS_PACKET,NDIS_BUFFER,VirtualAddress,这几个结构之间是什么关系,能不能给指导一下?我看这文章的遍数没你多。
按第一贴的“给分”键,给分。
游客

返回顶部