Virtual Miniport Driver Userspace IO

Hi there I’m trying to use a virtual miniport driver to pass traffic to a user space program but I can’t figure out how/where to setup the device to handle IRPs. All the examples I can find seem to do it in a way that doesn’t work for NDIS 6 and above. How should I setup the device if i can no longer use NdisMRegisterDevice? and
Where how/do I set the dispatch functions?

Thanks

Did you happen to read the MSDN page for NdisMRegisterDevice() and see the very clearly spelled out NDIS6 replacement called NdisRegisterDeviceEx()?

Dave Cattley

…Yes I had read that page and found that function. Perhaps I didn’t word my question properly but I was actually looking for information/help/resources/advice on HOW to use these functions. I don’t understand where to call NdisRegisterDeviceEx()

The virtual miniport adapter example has a different flow to the older passthru examples where i could understand the use of NdisRegisterDevice() and I cannot find any information on how to get information from it to a user space program.

PASSTHRU demonstrates two key points about registering a Control Device in an NDIS Miniport:

  1. It has to happen at PASSIVE_LEVEL *after* DriverEntry has returned.

  2. Tear-down has to happen at PASSIVE_LEVEL from an action before DriverUnload is called.

This is because a Miniport is a PnP driver and if you create a Control Device Object in Driver Entry, you will not get called at DriverUnload() since PnP will see that your driver has a non-empty Device Object list when it processed device removals.

So the same basic thing has to happen in an NDIS6 Miniport as with PASSTHRU (NDIS5).

a) Trigger the register your CDO with NdisMRegisterDeviceEx() during MiniportInitializeEx().
b) Synchronize with and wait for the completion of NdisMDerigisterDevice() in MiniportHaltEx()

Now PASSTHRU demonstrates (mostly) how to do this where the goal is to have one CDO shared by all Miniport instances with a lifetime bounded by and synchronized with the Miniport instance lifetimes. You could probably imagine a cleaner way to do that with a Wait Lock but in NDIS5 there was no such standard passive level lock in the NDIS DDI. But if you understand the psuedo-state machine in PASSTHRU you just need about the same thing.

If you want a CDO *per* Miniport instance then you just need to register it in MiniportInitializeEx() and deregister it in MiniportHalt(). Of course you have the chore of giving it a name.

You might also consider a lower filter in your device stack written with WDF and having your Miniport driver query down stack with QI to get an interface from the lower filter to send packets and get packet indications from (or use IRPs, whatever) and deal with all of the machinery of creating either device interfaces (cool) or a Control Device Object (cool enough) in KMDF where you can generally be sure it all is going to work without much fuss. Otherwise you have to deal with the old IoCsqXxxx cancel safe queue logic and build all sorts of nonsense to back fill what NDIS cuts you off from by not allowing you to delegate IRP processing to KMDF.

Good Luck,
Dave Cattley

>2. Tear-down has to happen at PASSIVE_LEVEL from an action before DriverUnload is called.

Ok, let me correct that. As I just read it in the bounce-back from the list I realize I did not get that precisely correct.

Tear-down has to happen at PASSIVE_LEVEL and complete before completing a MiniportHalt operation on the last Miniport Instance.

Again, this is all tied up in PnP otherwise getting freaked out and not ever calling MiniportUnload() and unloading your driver. Reboots are not friendly and to be frowned on.

Good Luck,
Dave Cattley

On 01-Jun-2015 16:33, rennie_3629 wrote:

Hi there I’m trying to use a virtual miniport driver to pass traffic to a user space program but I can’t figure out how/where to setup the device to handle IRPs. All the examples I can find seem to do it in a way that doesn’t work for NDIS 6 and above. How should I setup the device if i can no longer use NdisMRegisterDevice? and
Where how/do I set the dispatch functions?

For NDIS 6, use NdisRegisterDeviceEx.

  • pa

Thanks Dave I think I have the Device Object all set up correctly (at least it doesn’t blue screen anymore) I was staying away from having filter drivers as writing one driver has proved to be difficult enough.

I was only planning on having one adapter/driver ever at a time.

Where can I store data in the driver though? I’m looking for a method to store all the data sent through the mini-port so that I can send it to the application via the IRPs

>I was staying away from having filter drivers as writing one driver has proved to be difficult enough.

Writing a WDK filter driver with a CDO is a pretty simple exercise. Writing correct WDM cancel-safe I/O in an NDIS miniport is not so easy. It is all a trade off.

I was only planning on having one adapter/driver ever at a time.

Do you expect this simplification to somehow make it easier? IMO it rarely does.

Where can I store data in the driver though? I’m looking for a method to store all the data sent through the mini-port so that I can send it to the application via the IRPs

The application sends the IRPs to the driver. The buffer associated with the IRP is where the data for the network frame is placed.

I suggest you search for tun tap driver in the OSS world to see a simple example.

Good luck,
Dave Cattley

Thanks Dave, each one of your replies has proved more useful/insightful than days of reading the documentation.

I see your point as I don’t really understand how to do the cancel-safe part. However I also don’t know where I would start on the filter driver. Is the miniport/filter pair the correct model to accomplish this? I’m boggled that this is such a difficult functionality to attain

For example if I take the NDIS 6.0 Filter Driver example from the sdk samples it doesn’t look like I’ll have to do too much to make it work with the IRP style IO for a userspace program, however how would I have to adjust the miniport driver and the filter driver so that they interact properly?

Also from a logistical standpoint how does one go about developing a project like this? Should I keep my miniport driver project as is and then have a separate project for the filter driver? Then to test you have to deploy both, install the miniport then install the filter onto it?

Yes I’ve read through a couple Tun Tap drivers but can’t really understand their flow, presumably this is the adapter and then filter layered model?

Thanks again,
Graeme

Actually sorry this tap driver https://github.com/OpenVPN/tap-windows6 does seem to do it the way I was originally trying to.

Which approach (miniport and filter vs just miniport vs unknown other) would be more beginner friendly?

> I see your point as I don’t really understand how to do the cancel-safe part. However I also don’t know where I would start on the filter driver. Is the miniport/filter pair the correct model to accomplish this? I’m boggled that this is such a difficult functionality to attain

For example if I take the NDIS 6.0 Filter Driver example from the sdk samples it doesn’t look like I’ll have to do too much to make it work with the IRP style IO for a userspace program, however how would I have to adjust the miniport driver and the filter driver so that they interact properly?

Uggh. Sorry. I failed to clearly communicate that the “filter driver” I was talking about is *NOT* an NDIS Light Weight Filter driver but a PnP Filter driver. The last thing you need is a LWF.

A PnP Filter driver is a participant in the NT I/O device stack for a PnP device. Basically it can get involved in IRP processing.

A LWF driver is a participant in the NDIS data & control path of a network stack.

The idea of using a lower PnP Filter ‘below’ the Miniport is to separate your problem into two ‘concerns’ each in their own driver.

Concern #1 is interfacing to NDIS as a (virtual) NIC. This must be an NDIS Miniport driver and NDIS will own the NT I/O dispatch as the “Port” driver. You cannot, for example, use WDF to manage IRP processing.

Concern #2 is interfacing to NT I/O (your application via Read/Write/DeviceIoControl). Here it would be really quite helpful to your productivity and the correctness of the solution if you could leverage WDF to manage the IRP processing.

You could of course forego WDF and use the NDIS registered device (control device) mechanism and handle the IRP processing as one would in a WDM (old-school) driver and you could use the IoCsqXxx cancel-safe-queue helper routines to deal with I/O cancelation and request parking, etc.

But if you could separate those concerns into two separate drivers, one and NDIS Miniport and the other a WDF PnP driver then you could build both in their required or optimal models.

That is all I was trying to get across. Communicating between the NDIS Miniport and the PnP Filter is quite simple. There was once a sample (in older WDKs) that demonstrated this. It was an NDIS Miniport driver layered over a PnP driver that managed the actual hardware. I recall the samples were called VNETMINI and some corresponding E100BX driver.

Also from a logistical standpoint how does one go about developing a project like this? Should I keep my miniport driver project as is and then have a separate project for the filter driver? Then to test you have to deploy both, install the miniport then install the filter onto it?

The overall project is two drivers and one INF file combined to create a single “Driver Package”. It is a single logical driver as far as PnP is concerned and when it is installed on your virtual device both the Miniport and PnP Filter will be installed as a single logical action.

Yes I’ve read through a couple Tun Tap drivers but can’t really understand their flow, presumably this is the adapter and then filter layered model?

I doubt any of the public source TunTap drivers implement this model and the reason is primarily because they all have their lineage trailing back to NT4 and Win9x. :slight_smile:

Irrespective of the choice of one Miniport or Miniport and lower Filter the flow is the same:

  1. UM app opens some device for I/O.

  2. UM app pends Read (or IOCTL) requests on the device to give the driver requests to complete inbound NDIS frames into. In other words, this is where your Miniport Send path will deposit frames ; e.g. copy whatever part of the packet you are capturing into the buffer described by the I/O request. At that point the I/O request is ‘completed’ and your UM app can then do what it does. The UM app needs to keep pending Read requests of course.

  3. UM app issues a Write (or IOCTL) request on the device to give the driver a request describing a packet to inject into the Miniport Receive path. You can either copy and stage the data into a new buffer or you can get clever and describe the NetBuffer MDL chain to NDIS referencing the MDL chain from the I/O request. Just keep in mind that data copy is rarely the performance issue that people think it might be on modern CPUs and this type of problem. Also NDIS has some frame composition rules and frankly it is just easier to have a pool of NBLs that are all setup to hold frames for injection that are allocated ‘flat’ and just avoid all of the nonsense of assembling a MAC frame from whatever your UM app sends you.

  4. When your UM app is done, it cancels any pending I/O (the Reads) and waits for Sends to complete and closes the device object.

Some of the confusion in the examples you might be looking at might come from trying to implement a “signal and poll” model of read instead of the more efficient model of pending read requests. If you see any goofiness that involves the UM app and driver sharing an event handle, that is what is going on. If you see things like convoluted buffering schemes (ring buffers or mapped shared memory) it is just a complex attempt at dealing with a non-problem (memory copy speed).

Good Luck,
Dave Cattley

> Actually sorry this tap driver https://github.com/OpenVPN/tap-windows6 does seem to do it the way I was originally trying to.

Which approach (miniport and filter vs just miniport vs unknown other) would be more beginner friendly?

This is a fine example to learn how your “NDIS Minport Only” could be achieved.

This one (IIRC) creates a Control Device for each VNIC instance.

Good Luck,
Dave Cattley

Thanks Dave. That driver flow makes a lot of sense, though it looks like I’ve got a lot more reading to do as I aim to understand this WDF PnP Filter business.

I couldn’t find that old sample anywhere though. Is there any other samples you know of that have a filter driver of this sort? Either demonstrating How it works/how to build it or how to make it interact with my NDIS (presumably changes will have to be made there too)

> I couldn’t find that old sample anywhere though. Is there any other samples you know of that have a filter driver of this sort? Either demonstrating How it works/how to build it or how to make it interact with my NDIS (presumably changes will have to be made there too)

The samples are in the Vista / SRV2K8 WDK. The WDF versions are in the KMDF samples not under network/ndis.
The WDK samples have been reworked and pruned many times since then and those seem to have fallen off the list. The older WDKs can be downloaded from MSDN subscriber downloads. I don’t recall if they were available directly.
IIRC the sample was named NDISWDM (the WDM version) and was adapted as part of the KMDF samples.
It may have survived into the Win7 WDK which I believe is downloadable directly.
I’m sorry for the non-definitive answers. I am traveling and not equiped with access to my dev environment where I could have answered this with clear and short reply.
Good Luck,Dave Cattley

Regarding PnP and driver-to-driver comminication (including QI and other variants) this thread from the archives pretty much runs multiple laps around all of the sometimes befuddling issues and solutions:
https://www.osronline.com/showthread.cfm?link=180493
It contains a very memorable post by list regular MM :slight_smile:

[quote] PDO, FDO, FDO (filter DO), class, port, miniport, KMDF, UMDF, WDF, WDM, WMD,
driver, minidriver, driver/minidriver pair, port/miniport driver pair,
class/miniclass driver pair, ec2 keypair…all permuted over sets like SCSI,
Video, NDIS…plus ‘upper’ and ‘lower’…and ‘raw’ and ‘cooked’…factor in
bizzaro/cd burner stack world…throw in the essentially black boxes like ‘bus
driver’…and of course ‘bus filter’…but only if you’re an IHV…or an
OEM…otherwise, you’re SOL…

Hard to see how that could get confusing.

mmHard to imagine truer words being spoken …Cheers,Dave Cattley

Yes I couldn’t agree more with that.
It looks like the OSR example of a filter http://www.osronline.com/article.cfm?name=wdffltr_v10.zip&id=446 is what I’m looking for and I can understand its workings with respect to the application I/O but how to attach it to my miniport and make them communicate is beyond me. I found the ndiswdm example (in the wdk7) but from what I can see this is a new miniport driver but with a “wdm lower edge”.

Should I be adapting my miniport to act like that? Or the consensus (if you can call it that) of that thread seemed to be the QI, though MSDN said QI for ndis has been replaced by OID’s unless I’m looking at the wrong thing again. Perhaps I still have the wrong sample as this doesn’t seem to be the “quite simple” part. Most of the methods I can see seem to be handeling passing info between different driver stacks and whatnot. There must be an easier way I’ve missed to pass data between two drivers when they’re developed and installed as one.

Also I’ve created the filter driver project in my VS Solution and linked it to my driver package but I can’t see anyway of compiling them into a single .inf. Is there some method of calling or setting up the lower filter driver after you’ve setup your miniport driver?

> It looks like the OSR example of a filter http://www.osronline.com/article.cfm?name=wdffltr_v10.zip&id=446 is what I’m looking for and I can understand its workings with respect to the application I/O but how to attach it to my miniport and make them communicate is beyond me. I found the ndiswdm example (in the wdk7) but from what I can see this is a new miniport driver but with a “wdm lower edge”.

Ok, so the sample I was recalling yesterday was named NDISEDGE in the KMDF samples. I believe it is still in the Win7 WDK which you can download. But it is basically a NDIS Miniport with a KMDF (instead of WDM) lower edge. It rides on top of an uses the services of some other driver beneath it in the stack that implements the expected I/O interface the sample invented for this purpose. There is a corresponding KMDF sample for the E100BX that implements the expected I/O interface and then talks to an E100BX NIC.
This pair of samples as I recall use IRP based I/O and not a QI function pointer contract but it demonstrates how to combine an NDIS Miniport upper-edge with an NT I/O lower edge that then communicates to another (lower) driver in the stack which does the work of dealing with the NIC (or in your case, I/O interface to UM).

Should I be adapting my miniport to act like that? Or the consensus (if you can call it that) of that thread seemed to be the QI, though MSDN said QI for ndis has been replaced by OID’s unless I’m looking at the wrong thing again. Perhaps I still have the wrong sample as this doesn’t seem to be the “quite simple” part. Most of the methods I can see seem to be handeling passing info between different driver stacks and whatnot. There must be an easier way I’ve missed to pass data between two drivers when they’re developed and installed as one.

Ok, again, QI and NDIS OIDs have no relationship. Wherever that statement comes from is just noise. QI (or IRP_MN_QUERY_INTERFACE requests) is a PnP concept. OIDs are a NDIS control concept. The way to NT drivers in a PnP driver stack ‘communicate’ with one another is via in-stack I/O requests and one such request is a IRP_MN_QUERY_INTERFACE by which they can exchange function pointers or whatever.

Also I’ve created the filter driver project in my VS Solution and linked it to my driver package but I can’t see anyway of compiling them into a single .inf. Is there some method of calling or setting up the lower filter driver after you’ve setup your miniport driver?

Ok, so the INF will be just like any other NIC card INF with the addition of a second AddService, HKR,“LowerFilters” and CoServices stuff.
I think the NDISEDGE (and the original NDISWDM) sample INFs covered those cases or it could have been in the PCIDRV sample INF which is what was the ‘lower’ driver for them.
If you are comfortable with the single driver solution as demonstrated by the OSS driver you found then perhaps that type of solution is where you should start.
Good Luck,Dave Cattley