Copyright © 2003 by Printing Communications Assoc., Inc. (PCAUSA). All rights reserved
This is Part 2 of an ongoing series of articles that focus on extending the Microsoft® Windows® Driver Development Kit (DDK) PassThru NDIS Intermediate (IM) driver sample. This article will illustrate how to extend PassThru so that it can block packets sent to or received from specific IP addresses.
Part 1 addressed some of the "basics" needed to extend PassThru driver sample. The topics addressed in Part 1 included:
This article will illustrate how to extend the PassThru driver so that it has the capability to block packets sent to or received from a list blocked IP addresses. The basic "requirements" for IP Address Blocking driver are:
For this article each of us developed an NDIS IM driver from the requirements. The drivers are nearly identical, functionally: The directives passed to each driver are basically the same, and each driver yields basically the same behavior. There are, however, significant differences between the IP-address-blocking implementations. These differences are in summary:
Thomas | James | |
Driver API | DeviceIoControl (IOCTL) | Windows Management Instrumentation (WMI) |
Packet Header Definitions | Adopted from FreeBSD 5.0 | Various RFCs |
Each sample driver provides insight into an approach that satisfies the requirements. We think that seeing the two drivers will be instructive in showing particular techniques and in illustrating how the same requirements can be married with a common starting point to yield different designs and implementations.
This article consists of an overview of design issues, followed by comments by Thomas and James about their drivers and the companion applications. The “meat” of the article is the two sets of source code.
There are three points in PassThru where packets are seen, and these become the places that forward a packet to the next layer or drop (“block”) the packet by not forwarding it:
To make the point explicitly, dropping a packet is accomplished by not forwarding it to the next layer. There is no NDIS API to drop a packet; rather, the packet is not sent down or indicated up. (But in the case of packets heading down, every packet – whether sent further down or dropped – has to be completed (eventually) by NdisMSendComplete, so that the sender above the IM driver may recycle the packet’s resources.)
Note that in this sample MPSend is not actually employed because MPSendPackets is used. That is: one or the other can be specified in registering miniport callbacks. Registering miniport callbacks allows either the old-style or the new-style send callback to be selected at build-time.
After the PassThru driver is initially loaded subsequent NDIS operations may require the driver to be unloaded. For example, the user may use the Network Control Panel to remove or uncheck the PassThru driver.
It is important to insure that the overall design of the IP Blocking design does not prevent the PassThru driver from being unloaded on demand. If a handle to the driver is created from user-mode then either 1.) it should be left open only briefly or 2.) a means should be provided to notify the application that the handle must be closed in order for NDIS operations to proceed.
There are some serialization issues, because certain parts of the driver may run at PASSIVE_LEVEL and so be interrupted by other parts running at DISPATCH_LEVEL. A further consideration is the existence of MP (multiprocessor) systems, where two (or more) driver instances may be running simultaneously quite irrespectively of their IRQL. Spin locks are used in some cases; in other cases, it suffices for purposes of copying statistics counters merely to employ code that preserve cache coherency ("interlocked" family of functions).
These are simply choices made by the driver developer:
The interaction between the user-mode application and the kernel-mode driver is designed to be occasional. The application communicates briefly with the driver to pass control information; thereafter the driver operates autonomously until the next interaction with the application. The application may also occasionally query the driver for statistics information.
Both IOCTL and WMI are suitable interface mechanisms for passing the configuration and statistics information between the application and the companion driver. However, there are pros and cons with each interface approach. Here are a few:
Pros
Cons
Pros
Cons
Here are some additional comments concerning dropping packets in an NDIS IM filter driver:
The remainder of this article consists of comments by Thomas and James about their drivers and companion test applications. The “meat” of the article is the two sets of source code.
The starting point for this driver is the skeleton driver developed in Part 1 of this series. (Click here to view "Extending the PassThru NDIS Intermediate Driver - Part 1"). The base code was reorganized by adding a new module (filter.c) that isolates the actual filtering code from the basic. The goal here is to make it easier to implement different kinds of filters in the future by making changes to primarily to the filter module.
The key functions provided in filter.c are:
The TestIOCTL application reads a text file containing the adapter name and the list of IP addresses to block. The sorted list is passed to the driver using IOCTL_PTUSERIO_SET_IPv4_BLOCK_FILTER. In the FltDevIoControl routine the list of IP addresses is duplicated and then saved in the FilterReserved area of the ADAPT structure.
As packets enter the FltFilterXYZ routines sanity checks are performed and then the IP header is accessed to fetch the packet source or destination address, as appropriate. This samples uses the Internet-related headers from FreeBSD 5.0 to define IP-related constants and structures. For example, struct ip, defined in /B2Winet/ip.h, is referenced in the filters.
You will find a subset of the FreeBSD inet headers in the B2Winet folder. These have been lightly modified to work in the Windows software development environment. If you are interested in other headers (like IPv6) then you can get your own copy of FreeBSD and add the headers you need. You may also find these file on the Internet.
To view the list of adapters that have been bound to the PassThru NDIS IM driver just run the TestIOCTL application with the /enum option:
TestIOCTL /enum
You will see information similar to this example output:
PassThru User I/O Test Application Copyright (c) 2003 Printing Communications Assoc., Inc. (PCAUSA) All rights reserved.
Driver Bindings: "\Device\{B3B985AD-EB56-4F6A-8D16-131118E52131}" "\DEVICE\{9C9770B5-CFBC-41DF-BE1D-510CEC826190}" <-- Lower Adapter Name Description: " National Semiconductor Corp. DP83820 10/100/1000 GigPhyter PCI Adapter" Medium: 802.3 Mac address = 00-40-F4-00-07-B5 Media Connect Status: Connected
"\Device\{03BB2564-4AA2-4E9B-B251-79D6A69B461F}" "\DEVICE\NDISWANIP" Description: " NdisWan Adapter" Medium: 802.3 Mac address = A6-3E-20-52-41-53 Media Connect Status: Connected
To set a blocking list use this command:
TestIOCTL /set filename
Where filename is a file that contains the NDIS name of the lower adapter to be used followed by the list of dotted IP addresses to block on the adapter. Here is an example that is targeted to the "National Semiconductor Corp. DP83820 10/100/1000 GigPhyter PCI Adapter":
# # Sample IP Address Blocking List File # # Lines beginning with '#' are comments. Leading and trailing whitespace # is ignored. Empty lines are ignored. # # The first non-comment line must be the NDIS name of the lower adapter # that the list of IP addresses to block is intended. # \Device\{B3B985AD-EB56-4F6A-8D16-131118E52131}
# # Then comes the list of IP addresses to block. The IP addresses can # have a comment, if desirable. # 172.16.1.10 192.168.2.1 192.168.2.2 192.168.2.3 172.16.100.5 ... 192.168.2.4 ; Can have comments per IP address, if desirable...
If you want to return all adapters to the default state – namely, no IP addresses and so no filtering – do
TestIOCTL /setdflt
If you want view IP blocking statistics use this command:
TestIOCTL /stats
The test applications are Win32 console applications built using Visual Studio .NET.
Drivers are built under the Windows DDK released for Windows XP (first with its own compiler). Later versions of the DDK build environment are also suitable.
The starting point for this driver is the version of PassThru in the Win2003 Server (“build 3790”) DDK. It was a deliberate goal that a minimum of changes be introduced into that base, and those changes are ordinarily identified by comments of this form:
// This is a comment. ja, 31 Oct 2003.
Filtering code is found mostly in the new kernel modules FilterRtns.cpp, UtilRtns.c and WMIRtns.cpp. The user-space applications are in SetIPAddrArray.cpp, PassThruStats.cpp, SetSecurity.cpp and ReportError.cpp.
Note: FilterRtns.cpp and WMIRtns.cpp are nominally C++ (due to the file extension), but the language employed is almost entirely C in syntax and semantics. The user-space applications do really employ C++, mainly because the author had C++ example code for COM/DCOM/WMI APIs.
Setting the filter values and providing statistics are based on WMI. You will see the techniques in miniport.c, mostly in MPQueryInformation and MPSetInformation. But note that at the beginning of miniport.c, there is an array of the two GUIDs PassThru supports.
The inspection of packets, the dropping of them when required and the keeping of statistics are done in MPSendPackets, PTReceive and PTReceivePacket. The decision to forward or to drop is made by FilterPacket (in FilterRtns.cpp), a routine that MPSendPackets, PTReceive and PTReceivePacket call.
FilterPacket first determines whether the packet is an IPv4 packet. Next, FilterPacket gets a copy of the list of IP addresses for which packets are to be dropped. Finally, FilterPacket does a binary search of that list and says to drop the packet if the packet is for Send and the destination IP address matches or if the packet is for Receive and the source IP address matches.
To obtain the current list of IP addresses for the adapter pertaining to the concerned packet, FilterPacket calls PassthruWMIGetAddrArray (in WMIRtns.cpp). To set the list, MPSetInformation calls PassthruWMISetAddrArray. PassthruWMIGetAddrArray gets a read lock on the portion of the ADAPT structure pertaining to filtering; PassthruWMISetAddrArray gets an update lock. Thus, for a given adapter, a consistent picture of the IP addresses is obtained or created.
Note: There is no serialization of updating or reading IP-address lists across adapters. The author doesn’t feel this is worth the trouble or even that it makes sense. So it is conceivable that two instances of an application, running at the same time, could each try to update all adapters and yet instate different lists for different adapters. But for a given adapter, updating or copying the IP address list is an atomic operation.
There is no serialization of statistics counters. For purposes of statistics, the author does not believe such serialization would be worth the trouble. Statistics can, after all, change from one moment to the next. But to maintain cache coherency (so that a given counter is fetched or updated as a unit), statistics counters are read or updated atomically.
SetIPAddrArray is the user-space program to get/set filter addresses. The program to get statistics (packets seen, packets dropped) is PassThruStats.
For setting filters, SetIPAddrArray is hard-coded to use the file c:\Temp\passthru\AddrArray.txt. Each line in the file must contain a single dotted IP address, and the addresses must be in ascending order. Edit the file to contain the addresses you want. Then, if you want to affect all adapters on your system, do in a command window:
SetIPAddrArray set
If you want to see what IP address filters are in force on your system, do:
SetIPAddrArray get
If you want to set a specific adapter rather than all, copy the appropriate adapter description from a “get” operation and supply that description in double quotation marks. For example,
SetIPAddrArray set a="Linksys LNE100TX(v5) Fast Ethernet Adapter - Passthru Miniport"
When you next do a “get,” you will see the chosen adapter with the IP addresses you set, along with whatever was set previously for the other adapters.
If you want to return all adapters to the default state – namely, no IP addresses and so no filtering –, do:
SetIPAddrArray setdflt
PassThruStats is the application to get statistics from PassThru. To get statistics information use the commend:
PassThruStats get
This will report the numbers of packets seen and packets dropped in MPSendPackets, PTReceive and PTReceivePacket.
Both SetIPAddrArray and PassThruStats accept other parameters in addition to the “verb.” Those parameters indicate the system (if remote from that where the user-space program is running), the userid and password and, in the case of “set” with SetIPAddrArray, the name of the particular adapter to set. For example:
PassThruStats get MyFavoriteSystem MyUserid MyPassword
As already noted, PerfMon and WbemTest will work with PassThru since it uses WMI for its interface. One starts PerfMon, right-clicks on the data window, chooses Add Counters, chooses PassThru Statistics from the Performance object drop-down list and selects the particular measure(s) of interest.
WMI and MOF are a substantial subject. For drivers, WMI revolves around a .mof file to describe the data. PassThru.mof shows how to set up variables consisting of ULONGs. PassThru.mof is the basis for the header file passthru_wmi.h, which will be created as part of building the driver.
PassThru.mof is pointed to by PassThru.rc, the driver’s resource file. Note that in the resource file, the MOF file is designated by the operand NdisMofResource. This name, and not the name MofResource that is standard for WDM drivers, must be used. So far as this writer is aware, the need to employ this different form is nowhere documented.
The user-space programs use class names (resource information) corresponding to values in PassThru.mof.
The executables – the driver and the two user-space programs – are created via DDK build. But note that the sources files for the two latter refer to SDK files, so you may have to change something in those sources files; if you do, be sure you use no path names with imbedded blanks but rather the corresponding 8.3 names (see Building Windows Drivers for details). Visual Studio 6 project files are provided if anyone wants to build the user-space programs via VS.
This article and the companion code are intended to be used for the benefit of the reader of this article. The companion code is intended to help the reader derive and improve their own NDIS Intermediate driver for Windows platforms.
Here is the general form of the Copyright Notice found in the companion code for this article:
Companion Sample Code for the Article
"Extending the Microsoft PassThru NDIS Intermediate Driver"
Portions Copyright ©1992-2000 Microsoft Corporation; used by permission.
Portions Copyright © 2003 Printing Communications Associates, Inc. (PCAUSA)
The right to use this code in your own derivative works is granted so long as
This product includes software developed by PCAUSA. The name of PCAUSA may not be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
Portions of the IP Blocking Driver by Thomas include the following additional notice:
Copyright © 1982, 1986, 1993 The Regents of the University of California. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This article is:
Copyright © 2003 Printing Communications Associates, Inc. (PCAUSA). All rights reserved.
PCAUSA does not grant the right to redistribute or publish this article without written permission.
It is certainly intended that you should be able to incorporate the ideas and code presented in this article into your own code. At the same time, for a free code like this it is essential that you understand that the authors do not provide any warranty whatsoever that the sample code is fit for any purpose whatsoever.
In other words: This article and the accompanying sample code are provided for educational purposes only. Use at your own risk.
[Click here to download PassThruEx Part 2 source code... ]
When you unzip the sources the directory tree will be like this:
\PCADev
\NDISIM
\PassThruEx
\Part 2 - Root for PassThruEx Part 2 Sources
\Thomas - Root for IP Blocking Sources (Thomas...)
\TestIOCTL
\Driver
\B2Winet - BSD 5.0 inet Headers Adapted for Windows
\James - Root for IP Blocking Sources (James...)
\Readme.htm - Essential readme file
\inc
\PassThruStats
\SetIPAddrArray
\Sys
James Antognini is a software engineer in White Plains, New York, and a frequent contributor to online support forums. You can reach him at antognini@mindspring.com. You can find some of his work at http://home.mindspring.com/~antognini.
Thomas F. Divine is founder of PCAUSA, a company which has been serving the Windows device driver community since 1992. PCAUSA licenses network device driver samples that illustrate specialized kernel mode programming technologies such an NDIS Intermediate drivers, TDI Clients and a variety of network data filtering techniques. You can reach him at wd-3.tdivine@pcausa.com.