After the recent proposal submission for porting Wireguard to NetBSD, I felt I
needed to know more about kernel modules. Despite going against Cherry’s
(cherry@) advice to modify things directly in the kernel, I felt that doing a
port of wiregard to NetBSD might require some knowledge of how third-party
external code can interact with the kernel and request for it’s services.
The module(7)[1] man page was a good start on what is expected out of kernel
modules and the various types of kernel modules in NetBSD.
Quoting from the man page.
Kernel modules allow the system administrator to dynamically add and
remove functionality from a running system. This also helps software
developers add new parts of the kernel without constantly rebooting to
test their changes.
The wireguard kernel module, works pretty much this way in Linux, at the time of
it’s writing it can be loaded into the kernel using modprobe(8), the linux
kernel utility to load and unload dynamic kernel modules.
Hence my gut instinct to look and understand the working of a Kernel module in
NetBSD. This means that it “extends” the various kernel functionalities
incorporating “New” and “Experimental” features into the kernel without
modifying the kernel directly.
Going through the man page I understand there are 4 types of kernel module or as
they call it “Module Classes”.
Virtual File System modules
Virtual file systems may be added via the module interface.Device Driver modules
Many device drivers can be loaded as a kernel module. One potential
problem specific to block and character device drivers is that the device
nodes must exist for the devices to be accessed. These need to be created
manually, after the driver module has been successfully loaded. Most device
driver modules do not need any manual intervention to function properly.Execution Interpreters
Execution Interpreters can be loaded to provide support for executing
binaries not normally supported by the kernel. This also allows loading
support for executing foreign system binaries. Execution Interpreters may
require that an appropriate emulation module also be loaded.Miscellaneous modules
Miscellaneous modules are modules for which there are not currently well-
defined or well-used interfaces for extension. They are provided for
extension, and the user-provided module initialization routine is expected
to install the necessary “hooks” into the rest of the operating system. An
example of a “miscellaneous module” might be a loader for card-specific VGA
drivers or alternate terminal emulations in an appro- priately layered
console driver.Security-Model modules
Alternate system security models also may be loaded using module.
According the description what I am looking for here is not a “Security-Module”
or “Execution Interpreters” (thought this definitely looks interesting to me),
since we are not dealing with file systems we are not going to be looking at
“Virtual File Systems”, “Miscellaneous” does come close but our driver is not
something new or undefined, it is more abstract and looks like “Device
Driver”. More specifically it might resemble a tun(4) driver since Wireguard at
it’s core is a tunnel between two network interfaces.
Looking for some more material on how to write up an example kernel module I
came across this blog[2] by Saurav, describing how he wrote a simple device
driver that reads a byte of string and does a “Cryptogrpahically secure” random
permutation of the string. Going through his blog I figured out that writing
kernel modules may not exactly require extensive “arcane” knowledge of the
kernel mechanics, but one needs to understand the “primitives” provided by the
“framework” of the kernel.
By “primitives” I mean things like open(), close(), read(), write() which are
implemented in a kernel module and how they are expected to operate.
Testing a kernel module
After the unit test job for uvm_hotplug(9) I was quite happy with the way the
code was treated and actually made it into the NetBSD source tree.
Which brought me to thinking, if it was possible to test a kernel module. As of
the time of writing this blog, there is no standardized test methodology for
NetBSD. Couple of known ways of testing things out are
-
Regression testing for modules
Which involves writing up tests with shell scripts to test if the various
features of the module work as expected or not. -
Tesing modules via Rump unikernels
This involves loading up the module in rump instances and then run module
under the rumpkernel hence providing a sandbox environment to do tests.
Both are closely related and it is what you would call a “black box testing”.
In one of the upcoming posts, I shall talk about my ideas of how it might be
possible to unit test a kernel in userland.
References
- http://netbsd.gw.com/cgi-bin/man-cgi?module+7+NetBSD-current
- https://saurvs.github.io/post/writing-netbsd-kern-mod/