Search code examples
androidclinuxlinux-kernelnetlink

Kernel to userspace application communication


I am trying to make Kernel (Android, kernel 4.9.59) communicate with userspace applications. I found a solution using Netlink sockets: https://stackoverflow.com/a/25071310/4190159

The first issue with the solution is that struct netlink_skb_parms used in the solution does not have a member named 'pid', instead has a member named 'portid', which I believe is not the same as pid. Anyway, to compile my kernel side code/solution I used 'portid' member of struct netlink_skb_parms instead for initialization. However, now I am getting a different error.

My Kernel side Netlink socket code as follows:

#include <linux/sched.h>
//For netlink socket -->
#include <net/sock.h>
//#include <net/netlink.h>
#include <linux/netlink.h>
#include <linux/skbuff.h>
#include <linux/string.h>

#define MY_GROUP    1 //For netlink socket
struct sock* socket; //For netlink socket
struct sk_buff* socket_buff; //For netlink socket

static void nl_receive_callback (struct sk_buff *skb)
{
    nlmsg_free(skb);
}

static void kernel_send_nl_msg(void)
{
    struct nlmsghdr *nlsk_mh;
    char* msg = "hello from kernel";

    socket = netlink_kernel_create(&init_net, NETLINK_USERSOCK, 1, nl_receive_callback, NULL, THIS_MODULE);

    socket_buff = nlmsg_new(256, GFP_KERNEL);
    nlsk_mh = nlmsg_put(socket_buff, 0, 0, NLMSG_DONE, strlen(msg), 0);
    //NETLINK_CB(socket_buff).pid = 0;    // kernel pid is deprecated
    NETLINK_CB(socket_buff).portid = 0;
    NETLINK_CB(socket_buff).dst_group = MY_GROUP;
    strcpy(nlmsg_data(nlsk_mh), msg);

    nlmsg_multicast(socket, socket_buff, 0, MY_GROUP, GFP_KERNEL);
    pr_info("%s", msg);//Print out the message to kernel

    return;
}

My userspace application side code to intercept the message from the kernel as follows:

#include <sys/socket.h>
#include <linux/netlink.h>

#define MY_GROUP    1

void user_recieve_nl_msg(void)
{
    int sock_fd;
    struct sockaddr_nl user_sockaddr;
    struct nlmsghdr *nl_msghdr;
    struct msghdr msghdr;
    struct iovec iov;

    char* kernel_msg;

    sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_USERSOCK);

    memset(&user_sockaddr, 0, sizeof(user_sockaddr));
    user_sockaddr.nl_family = AF_NETLINK;
    user_sockaddr.nl_pid = getpid();
    user_sockaddr.nl_groups = MY_GROUP; 

    bind(sock_fd, (struct sockaddr*)&user_sockaddr, sizeof(user_sockaddr));
    while (1) {
        nl_msghdr = (struct nlmsghdr*) malloc(NLMSG_SPACE(256));
        memset(nl_msghdr, 0, NLMSG_SPACE(256));

        iov.iov_base = (void*) nl_msghdr;
        iov.iov_len = NLMSG_SPACE(256);

        msghdr.msg_name = (void*) &user_sockaddr;
        msghdr.msg_namelen = sizeof(user_sockaddr);
        msghdr.msg_iov = &iov;
        msghdr.msg_iovlen = 1;

        recvmsg(sock_fd, &msghdr, 0);

        kernel_msg = (char*)NLMSG_DATA(nl_msghdr);
        print("Kernel message: %s\n", kernel_msg); // print to android logs
    }

    close(sock_fd);
}

When I am trying to build the android kernel I am receiving the fllowing error:

kernel/sched/custom_code.h:34:65: error: passing argument 3 of 'netlink_kernel_create' makes pointer from integer without a cast [-Werror]

kernel/sched/custom_code.h:34:14: error: too many arguments to function 'netlink_kernel_create'

Note the code for the Kernel side is written in custom_code.h.

My questions are as follows:

  1. I have checked the function 'netlink_kernel_create' and I am sending the right number of arguments then why is the aformentioned error coming up? How this error could be resolved?

  2. What should I do to establish a valid communication between the kernel and the userspace application so that messages could be passed (to and fro) between them?


Solution

  • 1. Let's check netlink_kernel_create function in linux kernel:

    static inline struct sock *
    netlink_kernel_create(struct net *net, int unit, struct netlink_kernel_cfg *cfg)
    {
        return __netlink_kernel_create(net, unit, THIS_MODULE, cfg);
    }
    

    from here https://elixir.bootlin.com/linux/v4.9.59/source/include/linux/netlink.h#L60 Notice, that this function takes only 3 arguments ( instead of 6 in your code )

    This function have been changed in kernel 3.6 ( from 6 parameters to 4 ) https://elixir.bootlin.com/linux/v3.6/source/include/linux/netlink.h#L185

    And then renamed to __netlink_kernel_create in kernel 3.7

    netlink_kernel_create function from 3.7 accepts 3 arguments https://elixir.bootlin.com/linux/v3.7/source/include/linux/netlink.h#L48

    Try change this

    socket = netlink_kernel_create(&init_net, NETLINK_USERSOCK, 1, nl_receive_callback, NULL, THIS_MODULE);
    

    to this

    struct netlink_kernel_cfg cfg = {
        .input  = nl_receive_callback,
        .groups = 1,
    };
    socket = netlink_kernel_create(&init_net, NETLINK_USERSOCK, &cfg);
    
    1. Now you can send data in direction "kernel => application".

    When you start your application, it will bind socket with user_sockaddr.nl_groups = MY_GROUP; bind(sock_fd, (struct sockaddr*)&user_sockaddr, sizeof(user_sockaddr));.

    After this you can send data from kernel to application with NETLINK_CB(socket_buff).dst_group = MY_GROUP; nlmsg_multicast(socket, socket_buff, 0, MY_GROUP, GFP_KERNEL); and it will be received by application with recvmsg(sock_fd, &msghdr, 0);

    How can I call kernel_send_nl_msg() function to actually communicate with the userspace?

    You can call it from kernel module, which you write, compile and insmod into kernel. Or you can call it directly from kernel, but for this you will need to rebuild whole kernel.

    If you want to send data in direction "application = > kernel", then you need to do things in reverse: bind new socket with another MY_GROUP2 in kernel and send data from application with nlmsg_multicast