3

I have tried extending IOUserNetworkEthernet and calling RegisterEthernetInterface(). This works perfectly for one ethernet interface, though the driver crashes when RegisterEthernetInterface is called a second time (doesn't return an error code). I have tried registering with separate queues.

Another approach was extending IOUserClient instead, and calling IOService::Create to create child IOUserNetworkEthernet instances. Everything about this approach works (the children appear within ioreg). However, once I call RegisterEthernetInterface on just one of the children, macOS crashes.

How would I go about creating a dext with multiple ethernet interfaces? Have I been approaching it the right way?

Appreciate any help.

4

1 回答 1

2

I haven't yet implemented an ethernet dext myself, but based on my experience with using DriverKit for other types of drivers and knowing its design goals, I have an idea what the solution might be.

First off, let's clarify: you're implementing either a virtual ethernet device, or you're building a driver for hardware that unites multiple independent ports in one physical device. In the former case (I'm guessing this is what you're doing based on your IOUserClient comment), your driver will be matching IOUserResources and you create the ethernet driver instance when you receive the corresponding message from your user space component.

Now, DriverKit design: DriverKit is built around a goal of keeping the driver instance for each device in its own separate process. Sort of circling back to the microkernel idea. Specifically, this means that multiple devices that use the same driver will each create an independent instance of that driver in its own process. This very likely means that NetworkingDriverKit was never designed to support more than one instance of IOUserNetworkEthernet, because they are seen as separate devices. Hence the crashes.

OK, what do we do about it? Use DriverKit the way it was intended. Put each virtual ethernet adapter in its own driver instance. This gets a bit tricky. I think this should work:

  • Your dext has a "control" instance. This is the thing that matches IOUserResources. It's a simple IOService class which listens for the signal (presumably IOUserClient) to create or destroy a virtual ethernet device. When creating a virtual ethernet device, you need that to run in its own driver instance though. (For an actual device with multiple ports, this would match the USB/PCI device nub, manage bus communication with the device, and enumerate the ports.)
  • Instead of creating an instance of a IOUserNetworkEthernet subclass, create an instance of a "nub" class and attach it to your central control class instance. Call RegisterService() in its startup code, so it's considered for IOKit matching.
  • Set up a second IOKit matching dictionary in your Info.plist, which matches your new "nub" object. Here, use your IOUserNetworkEthernet-derived class to "drive" the nub. Once the virtual ethernet device is ready to use, call RegisterEthernetInterface().

2 extra things to note:

  1. The virtual device will run in a separate process from the control object, so they can only communicate via DriverKit inter-process calls, i.e. user clients. Hopefully, they don't really need to communicate much though, and the control client can pass all the information required via properties on the nub. If you're implementing support for multi-port hardware, you probably won't be able to avoid this part though.
  2. Your user space component (app, daemon) will need to open a new communication channel with the virtual device, so you'll probably need to implement user client support there too.

I'm not sure how you'd go about shutting down an individual virtual ethernet device, you could try calling Terminate() on either it or the nub it's matched on and see what happens.

于 2022-01-20T21:06:10.037 回答