Netlink socket support in SwiftNIO

We have a need to listen for interface up/down events in Linux and the normal way to accomplish this is using a netlink socket (See: netlink(7) - Linux manual page). My question is: does it seem appropriate to build netlink socket support into SwiftNIO?

As an experiment I attempted to create a netlink socket and use DatagramBootstrap.withBoundSocket to bootstrap this setup. However I ran into "unknown sockaddr family AF_NETLINK" errors. Pushing this even further I added netlink support to SocketAddress as a simple proof of concept and was able to properly receive and decode netlink messages. The commit for that proof of concept is here: PoC: Add netlink socket address support · Austinpayne/swift-nio@b043722 · GitHub

I realize that normally netlink is received over a raw socket and that my proof of concept breaks the public API so I do not think it is the proper way. I am looking for guidance from the NIO maintainers on how this might be implemented. My initial thoughts are that 1) we would implement a RawBootstrap class in NIO core that has less strict requirements for the socket address but would support netlink sockets (via sockaddr_nl) and 2) actual netlink message parsing code would be implemented outside of NIO core, probably in a third-party package.

Thanks for your input!

cc: @lukasa

Thanks for raising this!

Yes, I think it's appropriate to build netlink support into SwiftNIO. It's unfortunate that you've run into trouble with SocketAddress (though not entirely surprising, as we've hit this corner case ourselves a few times).

In the short term I think that we can consider netlink a special case and provide a direct NetlinkBootstrap. This could actually build directly on top of the DatagramChannel inside NIO, and simply add some special cases to it that allow it to tolerate being used in a "netlink mode" (avoiding attempting to populate local and remote address, for example). This would reduce the amount of code you needed to write.

I agree that the actual netlink parsing code should be done outside of NIO core.

Great, thanks for the tips Cory! I will take a look at this and see if I can get a PR opened soon.

Anyone interested in this still? I need to do it for my MRP implementation. Not much code here yet...

Yes, I think we'd continue to be interested in something that solves this problem. A good first step would be to create a package yourself that you can use as a proof-of-concept, and then we can do some helper reviews and try to discuss if-and-how we should integrate this with NIO.

Sure thing, it’s still part of SwiftMRP but I’ll factor it out into a separate package later. It is effectively just an async wrapper around libnl-3.

It would certainly be possible to bypass libnl-3 and talk to the kernel directly, if you were concerned about pulling in an LGPL dependency, but that just seems like extra work for now.

I think bypassing the LGPL dependency is useful, but not so much for license reasons (though that helps) but because it makes the build and install experience much more reliable, as we don't rely on having a system package present.

Agreed. It’s a chunk of work, but probably worth doing. I’ll add it to the list…

I'm very interested in this project as well and have had an internal implementation around libnl for quite some time now. Happy to contribute if there is a list of TODOs somewhere.

1 Like

A simpler approach I used elsewhere was just to monitor a PF_NETLINK / NETLINK_ROUTE socket and refresh interfaces whenever an event was received, this is super-easy to integrate with any existing code for interface discovery. Of course, it leaves you to calculate the changes yourself. I imagine though that if this is all you need, parsing the PF_NETLINK messages is also not difficult.

My use case (implementing MMRP, MVRP and MSRP from 802.1Q) requires APIs for dynamically configuring VLAN filtering, using NFLOG to process packets before the kernel drops them, etc. Implementing this all natively in Swift would be great, but it's effectively a reimplementation of libnl-3, and that's a pretty low priority for me right now. Not to say I won't do it at some point though, as I prefer to minimise my LGPL dependencies (beyond the C library, of course).

I broke it out into a separate library:

Patches to remove the libnl-3 dependency are welcome :) I'll get to it, eventually...

1 Like