55

I am trying to write a linux kernel module that communicates with user process using netlink. I am using netlink because the user program I want to communicate to communicates only using sockets and I can't change that to add ioctl() or anything.

I can't figure out how to do that, though! I found old examples like this one that are no longer valid for current kernel versions. I have also looked at this SO question but the sample here uses libnl for socket operations, and I want to stick to standard socket functions (defined by sys/socket.h). Can some one please guide me here to some tutorial or guide or some thing that can help me understand the interface and usage of netlink? I would highly appreciate a working example; nothing fancy, just a very basic example of how to establish a connection from a socket in user program to a socket in kernel and then send data from user process to kernel and receive back from kernel.

Please do not tell me to look at kernel code. I am already doing that, but it will take a lot of time and I don't have lot of it left.

After lot of trial and error I have following code which sends a message from a user program to the kernel, but the message from the kernel to the user program (using netlink_unicast()) is not working. It's not only not working, but the call hangs the systems and then I have to restart the machine. Can some one please take a look and tell me what I'm doing wrong? The netlink_unicast() call is commented in the following code. It should be uncommented for the kernel to user program message.

User Program

#include <sys/socket.h>  
#include <linux/netlink.h>  
#define NETLINK_USER 31  
#define MAX_PAYLOAD 1024  /* maximum payload size*/  

struct sockaddr_nl src_addr, dest_addr;  
struct nlmsghdr *nlh = NULL;  
struct iovec iov;  
int sock_fd;  
struct msghdr msg;  
 
void main()  
{  
    sock_fd=socket(PF_NETLINK, SOCK_RAW, NETLINK_USER);  
    if(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 */  
    /* interested in group 1<<0 */  
    bind(sock_fd, (struct sockaddr*)&src_addr,  
      sizeof(src_addr));  

    memset(&dest_addr, 0, sizeof(dest_addr));  
    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 = (struct nlmsghdr *)malloc(  
                          NLMSG_SPACE(MAX_PAYLOAD));  
    memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));  
    nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);  
    nlh->nlmsg_pid = getpid();  
    nlh->nlmsg_flags = 0;  

    strcpy(NLMSG_DATA(nlh), "Hello");  

    iov.iov_base = (void *)nlh;  
    iov.iov_len = nlh->nlmsg_len;  
    msg.msg_name = (void *)&dest_addr;  
    msg.msg_namelen = sizeof(dest_addr);  
    msg.msg_iov = &iov;  
    msg.msg_iovlen = 1;  

    printf("Sending message to kernel\n");  
    sendmsg(sock_fd,&msg,0);  
    printf("Waiting for message from kernel\n");  

    /* Read message from kernel */  
    recvmsg(sock_fd, &msg, 0);  
    printf(" Received message payload: %s\n",  
        NLMSG_DATA(nlh));  
    close(sock_fd);  
}

Kernel Code

#include <linux/module.h>  
#include <linux/kernel.h>  
#include <linux/init.h>  
#include <net/sock.h>  
#include <linux/socket.h>  
#include <linux/net.h>  
#include <asm/types.h>  
#include <linux/netlink.h>  
#include <linux/skbuff.h>  
 
#define NETLINK_USER 31  

struct sock *nl_sk = NULL;  

static void hello_nl_recv_msg(struct sk_buff *skb)  
{
        struct nlmsghdr *nlh;  
        int pid;  

        printk(KERN_INFO "Entering: %s\n", __FUNCTION__);  

        nlh=(struct nlmsghdr*)skb->data;  
        printk(KERN_INFO "Netlink received msg payload: %s\n",
            (char*)NLMSG_DATA(nlh));  
        pid = nlh->nlmsg_pid; /*pid of sending process */  
        NETLINK_CB(skb).dst_group = 0; /* not in mcast group */  
        NETLINK_CB(skb).pid = 0;      /* from kernel */  
        //NETLINK_CB(skb).groups = 0; /* not in mcast group */  
        //NETLINK_CB(skb).dst_pid = pid;  
        printk("About to send msg bak:\n");  
        //netlink_unicast(nl_sk,skb,pid,MSG_DONTWAIT);  

}  

static int __init hello_init(void)  
{  

        printk("Entering: %s\n",__FUNCTION__);  
        nl_sk=netlink_kernel_create(&init_net, NETLINK_USER, 0,
               hello_nl_recv_msg, NULL, THIS_MODULE);  
        if(!nl_sk)  
        {   
                printk(KERN_ALERT "Error creating socket.\n");  
                return -10;  
        }  
        return 0;  
}  

static void __exit hello_exit(void)  
{

        printk(KERN_INFO "exiting hello module\n");  
        netlink_kernel_release(nl_sk);  
}  

module_init(hello_init);  
module_exit(hello_exit);  
3
  • 1
    How could 10 people mark this as a favourite, but only 5 people upvote it? Commented Sep 4, 2013 at 15:10
  • I know it's a little bit late, but this book (ch 2) also discusses netlink sockets: amazon.com/Linux-Kernel-Networking-Implementation-Experts/dp/… Commented Apr 1, 2015 at 14:38
  • this user program works as long as struct msghdr msg; is defined in the global scope. But as soon as I move that inside a function (such as main), the user program no longer works and sendmsg returns -1 and errno is set to error 105 (ENOBUFS - no buffer space available). Can anyone explain why msghdr only works when defined globally in this program? Commented Mar 12, 2019 at 23:26

4 Answers 4

71

After reading kernel source I finally managed to make netlink sockets work for me. Below is an example of Netlink socket basics i.e opening a netlink socket, reading and writing to it and closing it.

Kernel Module

#include <linux/module.h>
#include <net/sock.h> 
#include <linux/netlink.h>
#include <linux/skbuff.h> 
#define NETLINK_USER 31

struct sock *nl_sk = NULL;

static void hello_nl_recv_msg(struct sk_buff *skb)
{

    struct nlmsghdr *nlh;
    int pid;
    struct sk_buff *skb_out;
    int msg_size;
    char *msg = "Hello from kernel";
    int res;

    printk(KERN_INFO "Entering: %s\n", __FUNCTION__);

    msg_size = strlen(msg);

    nlh = (struct nlmsghdr *)skb->data;
    printk(KERN_INFO "Netlink received msg payload:%s\n", (char *)nlmsg_data(nlh));
    pid = nlh->nlmsg_pid; /*pid of sending process */

    skb_out = nlmsg_new(msg_size, 0);
    if (!skb_out) {
        printk(KERN_ERR "Failed to allocate new skb\n");
        return;
    }

    nlh = nlmsg_put(skb_out, 0, 0, NLMSG_DONE, msg_size, 0);
    NETLINK_CB(skb_out).dst_group = 0; /* not in mcast group */
    strncpy(nlmsg_data(nlh), msg, msg_size);

    res = nlmsg_unicast(nl_sk, skb_out, pid);
    if (res < 0)
        printk(KERN_INFO "Error while sending bak to user\n");
}

static int __init hello_init(void)
{

    printk("Entering: %s\n", __FUNCTION__);
    //nl_sk = netlink_kernel_create(&init_net, NETLINK_USER, 0, hello_nl_recv_msg, NULL, THIS_MODULE);
    struct netlink_kernel_cfg cfg = {
        .input = hello_nl_recv_msg,
    };

    nl_sk = netlink_kernel_create(&init_net, NETLINK_USER, &cfg);
    if (!nl_sk) {
        printk(KERN_ALERT "Error creating socket.\n");
        return -10;
    }

    return 0;
}

static void __exit hello_exit(void)
{

    printk(KERN_INFO "exiting hello module\n");
    netlink_kernel_release(nl_sk);
}

module_init(hello_init); module_exit(hello_exit);

MODULE_LICENSE("GPL");

User Program

#include <linux/netlink.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>

#define NETLINK_USER 31

#define MAX_PAYLOAD 1024 /* maximum payload size*/
struct sockaddr_nl src_addr, dest_addr;
struct nlmsghdr *nlh = NULL;
struct iovec iov;
int sock_fd;
struct msghdr msg;

int main()
{
    sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_USER);
    if (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 */

    bind(sock_fd, (struct sockaddr *)&src_addr, sizeof(src_addr));

    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 = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));
    memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));
    nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
    nlh->nlmsg_pid = getpid();
    nlh->nlmsg_flags = 0;

    strcpy(NLMSG_DATA(nlh), "Hello");

    iov.iov_base = (void *)nlh;
    iov.iov_len = nlh->nlmsg_len;
    msg.msg_name = (void *)&dest_addr;
    msg.msg_namelen = sizeof(dest_addr);
    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;

    printf("Sending message to kernel\n");
    sendmsg(sock_fd, &msg, 0);
    printf("Waiting for message from kernel\n");

    /* Read message from kernel */
    recvmsg(sock_fd, &msg, 0);
    printf("Received message payload: %s\n", NLMSG_DATA(nlh));
    close(sock_fd);
}

Related thread about the magic constant NETLINK_USER 31: Can I have more than 32 netlink sockets in kernelspace?

Sign up to request clarification or add additional context in comments.

7 Comments

can you also plz add the README for others to build/run and test this and provide feedback.Thanks
How to compile ? Is their any option to provide while compile ?
Is it possible to run with user=31? I could only get it working with user = 0.
Is it possible to do netlink_kernel_create() within a function instead of the kernel init? So, that that function is a listener to userspace messages?
Are you not suppose to free skb_out ? I have just written a similar kernel Module, and its crashing when i try kfree_skb(skb_out).
|
9

Just in case anybody doesn't know how to compile, google "how to compile and load kernel module"

refer to http://www.cyberciti.biz/tips/compiling-linux-kernel-module.html

Grab kernel source code to which you'll compile module against http://kernel.org

Or simply update your headers if you are running intended kernel

# apt-get install kernel-headers-$(uname -r)

Create a makefile, for example

obj-m = hello.o
KVERSION = $(shell uname -r)
all:
        make -C /lib/modules/$(KVERSION)/build M=$(PWD) modules
clean:
        make -C /lib/modules/$(KVERSION)/build M=$(PWD) clean

Make and you'll get bunch of files. *.ko is the one you'll load into your kernel, run

# insmod hello.ko

if you us lsmod to check all loaded modules, you'll find yours, most likely you will see:

hello       12575  0 

In our case, compile and run user code:

gcc hello.c -o hello.o
./hello.o

If everything is OK, you'll get following message using binW's code:

Sending message to kernel
Waiting for message from kernel
 Received message payload: Hello from kernel

Finally, remove the module using:

# rmmod hello

Comments

7

It works for me with kernel 3.2. For kernel 3.6 & above, it needs a bit of a change at the netlink_kernel_create function.

 struct netlink_kernel_cfg cfg = {
                .groups = 1,
                .input = hello_nl_recv_msg,
        };
        printk("Entering: %s\n", __FUNCTION__);
        nl_sk = netlink_kernel_create(&init_net, NETLINK_USER, &cfg);

Comments

0

you need include following header file into client_side code:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

1 Comment

I've fixed it. Consider deleting this answer.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.