Netlink Socket program (from User space process to Kernel Space process)

Netlink is a Linux kernel socket interface, which is used for inter-process communication between the user space and the kernel, and also between different user space processes. It reduces dependence on system calls, ioctls and proc files. Netlink also helps preserve kernel purity.
There are various ways by which processes in the user space can communicate with the kernel. These are system calls, ioctl and the proc file system. The problem with system calls is that they are linked statically to kernel code. So, any new feature that is to be provided has to be compiled with the kernel, but with dynamic linking modules such as device drivers, any feature that these modules want to provide cannot always be pre-configured as a system call. Similarly, for every new feature, it is hard to provide communication through the file system. All these communication mechanisms also require that the processes initiate the communication the kernel cannot initiate the communication.


Create 3 files in a folder:

1. Do 'make all'
2. If sudo password is asked, enter that.
3. Run, ./netlinkKernel.ko 
4. do 'dmesg' to view messages received from userspace process and sent to user space process.

Neelkanth $cat Makefile 

KDIR := /lib/modules/$(shell uname -r)/build

obj-m += netlinkKernel.o
all:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
gcc netlinkUser.c -o netlinkUser
sudo insmod netlinkKernel.ko
clean:
rm -rf *.o *.ko *.mod.* *.cmd .module* modules* Module* .*.cmd .tmp*
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
sudo rmmod netlinkKernel.ko

Neelkanth $cat netlinkKernel.c 

/*
 ************************************************************************** 
 *    Steps to create netlink socket in kernel driver. Read and Write     *
 *    messages to userspace process via netlink socket.                   *
 **************************************************************************
 * 1. In 'module_init', create a netlink socket using                     
 *    "netlink_kernel_create". The ouput of this function returns a 
 *    "netlink socket id".
 *
 * 2. Pass "init_net" structure to "netlink_kernel_create" as 1st 
 *    argument. A struct net contains information about the network 
 *    namespace, a set of network resources available to processes.
 *    Note that there could be multiple network namespaces (i.e. 
 *    multiple instances of the networking stack), but most drivers 
 *    use the init_net namespace. 
 *    File location: "net/core/net_namespace.c"
 *
 * 3. Pass "netlink_kernel_cfg" structure as 3rd argument to 
 *    "netlink_kernel_create". These are the optional kernel netlink 
 *    parameters. 
 *
 * 4. In structure "netlink_kernel_cfg" passed to "netlink_kernel_create"
 *    register a callback function to receive and send messages to 
 *    userspace process over netlink socket.    
 *    "hello_nl_recv_msg"
 *
 **************************************************************************     
 */

/*
 ********************************************************************
 * How to receive messages from userspace process to kernel driver  * 
 * module over netlink socket and reply back to user space process  *
 ********************************************************************
 * 1. "hello_nl_recv_msg" is the registered callback function 
 *    that would receive incoming messages over netlink socket 
 *    from userspace process and can repond back over the same 
 *    netlink socket.
 *
 * 2. The message received by "hello_nl_recv_msg" is via structure
 *    sk_buff. 
 *    [this is "socket buffer" File: "include/linux/skbuff.h"] 
 *
 *    "sk_buff is socket buffer organized in form of double linked
 *    list and has some of the below members."
 *    struct sk_buff {
 *       struct sk_buff * next;
 *       struct sk_buff * prev;
 *       struct sock * sk;
 *       struct skb_timeval tstamp;
 *       struct net_device * dev;
 *       struct net_device * input_dev;
 *    }
 * 
 * 3. 
 *   Netlink messages consist of a byte stream with one or multiple
 *   'nlmsghdr' headers and associated payload.
 *   Extact 'nlmsghdr' from sk_buff and print the incoming message
 *   from user space process.
 *
 * 4. 'nlmsg_new' and 'sk_buff' -- create a new message to be sent to 
 *    userspace process over netlink socket.
 *
 * 5. nlmsg_put --> nlmsg_data --> nlmsg_unicast   
 *
 ********************************************************************     
 */

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


#define NETLINK_USER 31
struct sock *netlink_socket = 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..This is neelkanth from kernel driver module...";
    int res;

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

    /* Read the message content from 'sk_buff' structure */
    /*
     * Message received from User space process via Net link socket 
     */
    nlh = (struct nlmsghdr*)skb->data;
    /*
     * Print the message received from the user space process over NL socket
     */
    printk(KERN_INFO "Netlink received msg payload(from userspace):%s\n",
           (char*)nlmsg_data(nlh));
    pid = nlh->nlmsg_pid; /*pid of sending process */
    printk("PID of userspace process from which message is received: %d\n", pid);


    /*
     * Message sent from Kernel driver to Userspace process over Netlink socket
     */
    msg_size=strlen(msg);
    /* Create new message using Netlink msg new API */
    skb_out = nlmsg_new(msg_size, 0);
    if (!skb_out) {
        printk(KERN_ERR "Failed to allocate new skb\n");
        return;
    }
    /* write the message content into 'sk_buff' structure */
    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);
    /* Send a Unicast message to userspace process over netlink socket */
    res = nlmsg_unicast(netlink_socket, skb_out, pid);
    if (res < 0) {
        printk(KERN_INFO "Error while sending bak to user\n");
    }
   
    return;
}

/*
 * When insmod of the kernel module is done, this function is invoked.
 */
static int __init hello_init(void)
{
    printk("Entering: %s\n",__FUNCTION__);
    /* 
     * This is for 3.6 kernels and above.
     */
    struct netlink_kernel_cfg cfg = {
        .input = hello_nl_recv_msg,
    };

    /* Create netlink socket in the kernel driver */
    netlink_socket = netlink_kernel_create(&init_net, NETLINK_USER, &cfg);
    if (!netlink_socket) {
        printk(KERN_ALERT "Error creating socket.\n");
        return -10;
    }

    return 0;
}

/*
 * When rmmod of kernel module is done, this function is invoked. 
 */
static void __exit hello_exit(void)
{
    printk(KERN_INFO "exiting hello module\n");
    netlink_kernel_release(netlink_socket);

    return;
}

/* insmod */
module_init(hello_init);
/* rmmod  */
module_exit(hello_exit);

MODULE_LICENSE("GPL");

Neelkanth $cat netlinkUser.c 

#include <sys/socket.h>
#include <linux/netlink.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.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 = 0;
struct msghdr msg;

int main(void)
{
    memset(&src_addr, 0, sizeof(src_addr));
    memset(&dest_addr, 0, sizeof(dest_addr));

    printf("My process ID: %d\n", getpid());

    /* 
     * Create a netlink socket for communicating with kernel
     * driver module.
     */
    sock_fd=socket(PF_NETLINK, SOCK_RAW, NETLINK_USER);
    if (sock_fd < 0) {
        return -1;
    }

    /*
     * Set the parameters of netlink socket for sending unicast
     * message to linux kernel.  
     */
    src_addr.nl_family = AF_NETLINK;
    /* self pid */
    src_addr.nl_pid = getpid();   
    dest_addr.nl_family = AF_NETLINK;
    /* For Linux Kernel, PID will be 0 */
    dest_addr.nl_pid = 0;     
     /* 
      * For unicast message to Linux Kernel driver, 
      * set the below to 0 
      */
    dest_addr.nl_groups = 0; 

    /*  
     * Bind 'src addr' to netlink socket.  
     */
    bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr));

    /*
     * Build netlink message Header from userspace process
     * to linux kernel driver Module. 
     */
    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), "This is neelkanth from userspace");

    #if 0
    Build Final Message header.
     
    struct msghdr {
       void             __user *msg_name;    /* ptr to socket address structure */
       int              msg_namelen;         /* size of socket address structure */
       struct iovec     __user *msg_iov;     /* scatter/gather array */
       __kernel_size_t  msg_iovlen;          /* # elements in msg_iov */
       void             __user *msg_control; /* ancillary data */
       __kernel_size_t  msg_controllen;      /* ancillary data buffer length */
    unsigned int        msg_flags;           /* flags on received message */

    };

    #endif

    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;

    /* Send Message to Kernel */
    printf("Sending message to kernel\n");
    sendmsg(sock_fd, &msg, 0);

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

    return 0;
}