
Last year, I have usedNetlink 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 withNetlink.
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 theUnix domain sockets. Similarly to the Unix domain sockets, and unlikeINET 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 theAF_NETLINK socket family.Netlink is designed to be a more flexible successor toioctl; 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 macroMY_NETLINK 30
which defines my customized netlink protocol. One can also choose available protocols such likeNETLINK_ROUTE orNETLINK_INET_DIAG. All available protocols can be seen inthis page. Functionnetlink_kernel_create() creates a netlink socket for user space application to communicate with. More information about netlink programming can foundhere.
netlink_kernel.c
#include<linux/module.h>#include<net/sock.h>#include<linux/netlink.h>#include<linux/skbuff.h>/* 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(structsk_buff*skb){structnlmsghdr*nlhead;structsk_buff*skb_out;intpid,res,msg_size;char*msg="Hello msg from kernel";printk(KERN_INFO"Entering: %s\n",__FUNCTION__);msg_size=strlen(msg);nlhead=(structnlmsghdr*)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_outif(!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 skbNETLINK_CB(skb_out).dst_group=0;strncpy(nlmsg_data(nlhead),msg,msg_size);//char *strncpy(char *dest, const char *src, size_t count)res=nlmsg_unicast(nl_sk,skb_out,pid);if(res<0)printk(KERN_INFO"Error while sending back to user\n");}staticint__initmyNetLink_init(void){structnetlink_kernel_cfgcfg={.input=myNetLink_recv_msg,};/*netlink_kernel_create() returns a pointer, should be checked with == NULL */nl_sk=netlink_kernel_create(&init_net,MY_NETLINK,&cfg);printk("Entering: %s, protocol family = %d\n",__FUNCTION__,MY_NETLINK);if(!nl_sk){printk(KERN_ALERT"Error creating socket.\n");return-10;}printk("MyNetLink Init OK!\n");return0;}staticvoid__exitmyNetLink_exit(void){printk(KERN_INFO"exiting myNetLink module\n");netlink_kernel_release(nl_sk);}module_init(myNetLink_init);module_exit(myNetLink_exit);MODULE_LICENSE("GPL");
User space application
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.
"netlink_client.c"
#include<sys/types.h>#include<unistd.h>#include<stdio.h>#include<stdlib.h>#include<sys/socket.h>#include<sys/types.h>#include<string.h>#include<asm/types.h>#include<linux/netlink.h>#include<linux/socket.h>#include<errno.h>#define NETLINK_USER 30 // same customized protocol as in my kernel module#define MAX_PAYLOAD 1024 // maximum payload sizestructsockaddr_nlsrc_addr,dest_addr;structnlmsghdr*nlh=NULL;structnlmsghdr*nlh2=NULL;structmsghdrmsg,resp;// famous struct msghdr, it includes "struct iovec * msg_iov;"structioveciov,iov2;intsock_fd;intmain(intargs,char*argv[]){//int socket(int domain, int type, int protocol);sock_fd=socket(PF_NETLINK,SOCK_RAW,NETLINK_USER);//NETLINK_KOBJECT_UEVENTif(sock_fd<0)return-1;memset(&src_addr,0,sizeof(src_addr));src_addr.nl_family=AF_NETLINK;src_addr.nl_pid=getpid();/* self pid *///int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);if(bind(sock_fd,(structsockaddr*)&src_addr,sizeof(src_addr))){perror("bind() error\n");close(sock_fd);return-1;}memset(&dest_addr,0,sizeof(dest_addr));dest_addr.nl_family=AF_NETLINK;dest_addr.nl_pid=0;/* For Linux Kernel */dest_addr.nl_groups=0;/* unicast *///nlh: contains "Hello" msgnlh=(structnlmsghdr*)malloc(NLMSG_SPACE(MAX_PAYLOAD));memset(nlh,0,NLMSG_SPACE(MAX_PAYLOAD));nlh->nlmsg_len=NLMSG_SPACE(MAX_PAYLOAD);nlh->nlmsg_pid=getpid();//self pidnlh->nlmsg_flags=0;//nlh2: contains received msgnlh2=(structnlmsghdr*)malloc(NLMSG_SPACE(MAX_PAYLOAD));memset(nlh2,0,NLMSG_SPACE(MAX_PAYLOAD));nlh2->nlmsg_len=NLMSG_SPACE(MAX_PAYLOAD);nlh2->nlmsg_pid=getpid();//self pidnlh2->nlmsg_flags=0;strcpy(NLMSG_DATA(nlh),"Hello this is a msg from userspace");//put "Hello" msg into nlhiov.iov_base=(void*)nlh;//iov -> nlhiov.iov_len=nlh->nlmsg_len;msg.msg_name=(void*)&dest_addr;//msg_name is Socket name: destmsg.msg_namelen=sizeof(dest_addr);msg.msg_iov=&iov;//msg -> iovmsg.msg_iovlen=1;iov2.iov_base=(void*)nlh2;//iov -> nlh2iov2.iov_len=nlh2->nlmsg_len;resp.msg_name=(void*)&dest_addr;//msg_name is Socket name: destresp.msg_namelen=sizeof(dest_addr);resp.msg_iov=&iov2;//resp -> iovresp.msg_iovlen=1;printf("Sending message to kernel\n");intret=sendmsg(sock_fd,&msg,0);printf("send ret: %d\n",ret);printf("Waiting for message from kernel\n");/* Read message from kernel */recvmsg(sock_fd,&resp,0);//msg is also receiver for readprintf("Received message payload: %s\n",(char*)NLMSG_DATA(nlh2));charusermsg[MAX_PAYLOAD];while(1){printf("Input your msg for sending to kernel: ");scanf("%s",usermsg);strcpy(NLMSG_DATA(nlh),usermsg);//put "Hello" msg into nlhprintf("Sending message\" %s\" to kernel\n",usermsg);ret=sendmsg(sock_fd,&msg,0);printf("send ret: %d\n",ret);printf("Waiting for message from kernel\n");/* Read message from kernel */recvmsg(sock_fd,&resp,0);//msg is also receiver for readprintf("Received message payload: %s\n",(char*)NLMSG_DATA(nlh2));}close(sock_fd);return0;}
Makefile
Create a Makefile with the following content which will enable us to easily compile the source files.
Makefile
obj-m += netlink_kernel.o#generate the pathCURRENT_PATH:=$(shellpwd)#the current kernel version numberLINUX_KERNEL:=$(shelluname-r)#the absolute pathLINUX_KERNEL_PATH:=/usr/src/linux-headers-$(LINUX_KERNEL)#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 filesall: @echo$(LINUX_KERNEL_PATH) make-C$(LINUX_KERNEL_PATH)M=$(CURRENT_PATH) modulesclient: gcc netlink_client.c-o netlink_client-g#cleanclean: make-C$(LINUX_KERNEL_PATH)M=$(CURRENT_PATH) cleanrmnetlink_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:
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:
sudoinsmod netlink_kernel.ko
Execute in a new terminal the following cmd to monitor the kernel messages:
dmesg -Hw
Now start the user application to start the communication:
./netlink_client
Top comments(1)
For further actions, you may consider blocking this person and/orreporting abuse