I’ve been looking into various ways to create a network filtering framework and
finally decided on implementing it as a TDI service. I decided against doing an
NDIS IM driver as I think it’s too deep into the stack and an LSP (which I used
to create a quickie prototype) has too many issues with other poorly written
LSP’s and the fact that it can be bypassed by implicitly providing the GUID of
the base provider.
Although there’s a serious lack of good documentation for building TDI drivers
I think I’ve pulled enough information from usenet, whitepapers, and the
MSDN/DDK docs for a good start. After a couple of days of work I’ve managed to
get a basic TDI together that attaches to \device\tcp, \device\udp, and
\device\rawip under XP - and it only seems to have problems when turning the
service off and unloading the driver.
Since I’m fairly new to writing TDI’s I’m hoping for a bit of direction and
comments on the approach i’ve using. Rather than posting the entire source on
the first go around I figured I would post the operational blueprints I wrote
before implementing the driver:
Operations performed during driver initialization.
- Create a control device via IoCreateDevice with an extension
block containing original information about other devices the
driver has attached to. the extension block contains the
following information:
a. Device and symbolic names of the driver
b. Pointers to original device, unnamed proxies, and
attached device handled (see below).
-
Create a symbolic link for the control device
-
Fill in the dispatch array to point to a generic handler.
-
Set DriverUnload to point to the unload and cleanup function.
-
Attach to desired devices. In this case attachments are made
to \device\TCP, \device\UDP, and \device\RAWIP devices. This
is done in the following steps:
a. Create an unnamed ‘proxy’ device.
b. Set the DO_DIRECT_IO flag (as recommended for devices
that can transfer large ammounts of data at a time).
>>> Should probably take the exact flags from the
device we’re attaching to <<<
c. Attach the proxy device to the target device
(i.e. \device\tcp, \device\udp, \device\rawip)
d. Save the pointer to the original target device.
- Initialize the TDI event handlers for connect, send, recv,
sendto, recvfrom, etc.
Operations performed during IRP dispatch
-
If the IRP is null simple return success.
-
Make the required call to IoGetCurrentIrpStackLocation()
-
If the IRP device is the primary device created during
initialization Set the io status to success, call
IoCompleteRequest(), and return.
-
If the IRP device is one of the original target devices
attached to a proxy do the following:
a If the current stack location is 0 or 1 skip the
current IRP via IoSkipCurrentIrpStackLocation().
Otherwise get the next irp on the stack via
IoGetNextIrpStackLocation (and copy the contents
of the current IRP into the ‘next IRP’)
b. If necessary set an IO completion routine.
c. Call the target device (obtained when attaching
the proxy device to the target).
So far everything works just dandy with the exception of unloading the driver
but I suspect it has to do with how I’m handling the IRP’s and io completions.
Are there any gotcha’s or issues that I might encounter with the above
approach.
Do You Yahoo!?
Tired of spam? Yahoo! Mail has the best spam protection around
http://mail.yahoo.com
Looks like a decent approach. Some comments:
1.) Don’t worry about unloading a TDI filter driver. Simply put: you can’t.
The operation of your filter will necessarily replace some external callback
and (probably) context structures with ones internal to your filter. There
is no reliable way to inform other components above or below you that you
are unloading.
2.) If you are actively filtering (modifying) receive data, then when a
client attempts to register a chained receive handler, don’t allow it
(ignore it). Force the client to use the non-chained receive mechanism. The
reason for this suggestion is that if you modify data passed to a client’s
chained receive handler, then the client can call TdiReturnChainedReceives
with your data - which you can’t reliably hook. You don’t want this! (This
parallel’s the NDIS wrapper function NdisReturnPackets used in a NDI
filter…). Performance will suffer in some cases, of course.
You might reconsifer the use of an NDIS IM driver… Or a combination of a
simple TDI filter that just provides process information in conjunction with
a NDIS IM filter that does data modification.
Good luck,
Thomas F. Divine, Windows DDK MVP
http://www.pcausa.com
“Chet” wrote in message news:xxxxx@ntdev…
>
> I’ve been looking into various ways to create a network filtering
> framework and
> finally decided on implementing it as a TDI service. I decided against
> doing an
> NDIS IM driver as I think it’s too deep into the stack and an LSP (which I
> used
> to create a quickie prototype) has too many issues with other poorly
> written
> LSP’s and the fact that it can be bypassed by implicitly providing the
> GUID of
> the base provider.
>
> Although there’s a serious lack of good documentation for building TDI
> drivers
> I think I’ve pulled enough information from usenet, whitepapers, and the
> MSDN/DDK docs for a good start. After a couple of days of work I’ve
> managed to
> get a basic TDI together that attaches to \device\tcp, \device\udp, and
> \device\rawip under XP - and it only seems to have problems when turning
> the
> service off and unloading the driver.
>
> Since I’m fairly new to writing TDI’s I’m hoping for a bit of direction
> and
> comments on the approach i’ve using. Rather than posting the entire source
> on
> the first go around I figured I would post the operational blueprints I
> wrote
> before implementing the driver:
>
>
>
>
> Operations performed during driver initialization.
> --------------------------------------------------
>
> 1. Create a control device via IoCreateDevice with an extension
> block containing original information about other devices the
> driver has attached to. the extension block contains the
> following information:
>
> a. Device and symbolic names of the driver
> b. Pointers to original device, unnamed proxies, and
> attached device handled (see below).
>
> 2. Create a symbolic link for the control device
>
> 3. Fill in the dispatch array to point to a generic handler.
>
> 4. Set DriverUnload to point to the unload and cleanup function.
>
> 5. Attach to desired devices. In this case attachments are made
> to \device\TCP, \device\UDP, and \device\RAWIP devices. This
> is done in the following steps:
>
> a. Create an unnamed ‘proxy’ device.
> b. Set the DO_DIRECT_IO flag (as recommended for devices
> that can transfer large ammounts of data at a time).
> >>> Should probably take the exact flags from the
> device we’re attaching to <<<
> c. Attach the proxy device to the target device
> (i.e. \device\tcp, \device\udp, \device\rawip)
> d. Save the pointer to the original target device.
>
> 6. Initialize the TDI event handlers for connect, send, recv,
> sendto, recvfrom, etc.
>
>
>
>
>
> Operations performed during IRP dispatch
> ----------------------------------------
>
> 1. If the IRP is null simple return success.
>
> 2. Make the required call to IoGetCurrentIrpStackLocation()
>
> 3. If the IRP device is the primary device created during
> initialization Set the io status to success, call
> IoCompleteRequest(), and return.
>
> 4. If the IRP device is one of the original target devices
> attached to a proxy do the following:
>
> a If the current stack location is 0 or 1 skip the
> current IRP via IoSkipCurrentIrpStackLocation().
> Otherwise get the next irp on the stack via
> IoGetNextIrpStackLocation (and copy the contents
> of the current IRP into the ‘next IRP’)
> b. If necessary set an IO completion routine.
> c. Call the target device (obtained when attaching
> the proxy device to the target).
>
>
>
> So far everything works just dandy with the exception of unloading the
> driver
> but I suspect it has to do with how I’m handling the IRP’s and io
> completions.
> Are there any gotcha’s or issues that I might encounter with the above
> approach.
>
>
>
>
> __________________________________________________
> Do You Yahoo!?
> Tired of spam? Yahoo! Mail has the best spam protection around
> http://mail.yahoo.com
>
>
TDI filters are not documented at all, and are definitely harder then NDIS
IMs.
Maxim Shatskih, Windows DDK MVP
StorageCraft Corporation
xxxxx@storagecraft.com
http://www.storagecraft.com
----- Original Message -----
From: “Chet”
To: “Windows System Software Devs Interest List”
Sent: Tuesday, January 10, 2006 7:42 AM
Subject: [ntdev] Basic info on TDI’s and attaching to other devices
>
> I’ve been looking into various ways to create a network filtering framework
and
> finally decided on implementing it as a TDI service. I decided against doing
an
> NDIS IM driver as I think it’s too deep into the stack and an LSP (which I
used
> to create a quickie prototype) has too many issues with other poorly written
> LSP’s and the fact that it can be bypassed by implicitly providing the GUID
of
> the base provider.
>
> Although there’s a serious lack of good documentation for building TDI
drivers
> I think I’ve pulled enough information from usenet, whitepapers, and the
> MSDN/DDK docs for a good start. After a couple of days of work I’ve managed
to
> get a basic TDI together that attaches to \device\tcp, \device\udp, and
> \device\rawip under XP - and it only seems to have problems when turning the
> service off and unloading the driver.
>
> Since I’m fairly new to writing TDI’s I’m hoping for a bit of direction and
> comments on the approach i’ve using. Rather than posting the entire source on
> the first go around I figured I would post the operational blueprints I wrote
> before implementing the driver:
>
>
>
>
> Operations performed during driver initialization.
> --------------------------------------------------
>
> 1. Create a control device via IoCreateDevice with an extension
> block containing original information about other devices the
> driver has attached to. the extension block contains the
> following information:
>
> a. Device and symbolic names of the driver
> b. Pointers to original device, unnamed proxies, and
> attached device handled (see below).
>
> 2. Create a symbolic link for the control device
>
> 3. Fill in the dispatch array to point to a generic handler.
>
> 4. Set DriverUnload to point to the unload and cleanup function.
>
> 5. Attach to desired devices. In this case attachments are made
> to \device\TCP, \device\UDP, and \device\RAWIP devices. This
> is done in the following steps:
>
> a. Create an unnamed ‘proxy’ device.
> b. Set the DO_DIRECT_IO flag (as recommended for devices
> that can transfer large ammounts of data at a time).
> >>> Should probably take the exact flags from the
> device we’re attaching to <<<
> c. Attach the proxy device to the target device
> (i.e. \device\tcp, \device\udp, \device\rawip)
> d. Save the pointer to the original target device.
>
> 6. Initialize the TDI event handlers for connect, send, recv,
> sendto, recvfrom, etc.
>
>
>
>
>
> Operations performed during IRP dispatch
> ----------------------------------------
>
> 1. If the IRP is null simple return success.
>
> 2. Make the required call to IoGetCurrentIrpStackLocation()
>
> 3. If the IRP device is the primary device created during
> initialization Set the io status to success, call
> IoCompleteRequest(), and return.
>
> 4. If the IRP device is one of the original target devices
> attached to a proxy do the following:
>
> a If the current stack location is 0 or 1 skip the
> current IRP via IoSkipCurrentIrpStackLocation().
> Otherwise get the next irp on the stack via
> IoGetNextIrpStackLocation (and copy the contents
> of the current IRP into the ‘next IRP’)
> b. If necessary set an IO completion routine.
> c. Call the target device (obtained when attaching
> the proxy device to the target).
>
>
>
> So far everything works just dandy with the exception of unloading the driver
> but I suspect it has to do with how I’m handling the IRP’s and io
completions.
> Are there any gotcha’s or issues that I might encounter with the above
> approach.
>
>
>
>
> __________________________________________________
> Do You Yahoo!?
> Tired of spam? Yahoo! Mail has the best spam protection around
> http://mail.yahoo.com
>
>
> —
> Questions? First check the Kernel Driver FAQ at
http://www.osronline.com/article.cfm?id=256
>
> You are currently subscribed to ntdev as: xxxxx@storagecraft.com
> To unsubscribe send a blank email to xxxxx@lists.osr.com
— “Thomas F. Divine” wrote:
> 1.) Don’t worry about unloading a TDI filter driver. Simply
> put: you can’t. The operation of your filter will necessarily
> replace some external callback and (probably) context
> structures with ones internal to your filter. There is no
> reliable way to inform other components above or below you that
> you are unloading.
I’ve actually reworked the service so that it loads an external dll and inserts
in into the stack. When unloading the ‘plugin’ the original service resets
itself and handles all of the necessary IRP’s and requests. Works quite well
but not 100% as I di end up getting BSOD every once in a while. Since the
plugins will play a major part of the commercial product it’s been good to see
where some of hte glitches are at the current stage of development.
> 2.) If you are actively filtering (modifying) receive data,
> then when a client attempts to register a chained receive
> handler, don’t allow it (ignore it). Force the client to use
[—SNIP—]
> You might reconsifer the use of an NDIS IM driver… Or a
> combination of a simple TDI filter that just provides process
> information in conjunction with a NDIS IM filter that does data
> modification.
I’ve implemened both the NDIS and TDI drivers and services but but am a little
convened about the performance of the NDIS driver. While the upstream doesn’t
appear to be accected much the downstream drops from 4mbps to about 3.8kbps.
This is the same across the board using public dsl/cable tests as well as
pulling data from local servers as well as remote dedicated servers (located in
Houston) that I operate.
The one option that I am considering is to use a single TDI service that
redirects specific connections running as proxies on the localhost. Something
similar to using WSPDuplicateSocket, passing that on to the Proxy device and
opening a new connection to the proxy and associating it with the original
connection. Not sure how well this will work but it does appear to have some
measure of success.
I’m wondering if anyone else has attempted this approach and what type of
results and/or headaches they encountered.
__________________________________________________
Do You Yahoo!?
Tired of spam? Yahoo! Mail has the best spam protection around
http://mail.yahoo.com