Last year, I have used Netlink in my development work for collecting some events from kernel space. I present in this post some basic practice I have done when I learnt to program with Netlink.
Introduction
Netlink is a Linux kernel interface used for inter-process communication (IPC) between both the kernel and userspace processes, and between different userspace processes, in a way similar to the Unix domain sockets. Similarly to the Unix domain sockets, and unlike INET sockets, Netlink communication cannot traverse host boundaries. Netlink provides a standard socket-based interface for userspace processes, and a kernel-side API for internal use by kernel modules. Originally, Netlink used the AF_NETLINK socket family. Netlink is designed to be a more flexible successor to ioctl; RFC 3549 describes the protocol in detail.
My practice
Kernel module
Below is the content of my source file “netlink_kernel.c” which yields a kernel module. There is macro MY_NETLINK 30 which defines my customized netlink protocol. One can also choose available protocols such like NETLINK_ROUTE or NETLINK_INET_DIAG. All available protocols can be seen in this page. Function netlink_kernel_create() creates a netlink socket for user space application to communicate with. More information about netlink programming can found here.
/* refer to https://elixir.bootlin.com/linux/v5.15.13/source/include/linux/netlink.h */
#define MY_NETLINK 30 // cannot be larger than 31, otherwise we shall get "insmod: ERROR: could not insert module netlink_kernel.ko: No child processes"
structsock *nl_sk =NULL;
staticvoidmyNetLink_recv_msg(struct sk_buff *skb) { structnlmsghdr *nlhead; structsk_buff *skb_out; int pid, res, msg_size; char *msg = "Hello msg from kernel";
printk(KERN_INFO "Entering: %s\n", __FUNCTION__);
msg_size = strlen(msg);
nlhead = (struct nlmsghdr*)skb->data; //nlhead message comes from skb's data... (sk_buff: unsigned char *data)
printk(KERN_INFO "MyNetlink has received: %s\n",(char*)nlmsg_data(nlhead));
pid = nlhead->nlmsg_pid; // Sending process port ID, will send new message back to the 'user space sender'
skb_out = nlmsg_new(msg_size, 0); //nlmsg_new - Allocate a new netlink message: skb_out
if(!skb_out) { printk(KERN_ERR "Failed to allocate new skb\n"); return; }
nlhead = nlmsg_put(skb_out, 0, 0, NLMSG_DONE, msg_size, 0); // Add a new netlink message to an skb NETLINK_CB(skb_out).dst_group = 0;
Below are the content of my file “netlink_client.c”. This program shall open a netlink socket with the same protocol and allow user to send/receive msg to/from kernel module in previous section.
#complie object # extension of "make modules" cmd with -C option and "M=dir" configuration # this cmd will switch working directory to the given path followed by the -C option # and will search specified source files from the given path configured by "M=" # and compile them to generate ko files
all: @echo $(LINUX_KERNEL_PATH) make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
client: gcc netlink_client.c -o netlink_client -g
#clean clean: make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean rm netlink_client
Test the communication
Make sure that files “netlink_kernel.c”, “netlink_client.c” and “Makefile” are in the same directory. In a Linux terminal window, cd into this directory and start the compilation:
1 2
make make client
Normally kernel module file “netlink_kernel.ko” and user application file “netlink_client” will be generated.
Load the generated kernel module “netlink_kernel.ko” to linux kernel:
1
sudo insmod netlink_kernel.ko
Execute in a new terminal the following cmd to monitor the kernel messages:
1
dmesg -Hw
Now start the user application to start the communication: