8

Hi, I've been working on trying to get Netlink sockets to work for the 3.2 linux kernel, but I cannot seem to figure out how to do it. I have looked around for examples of the basics of Netlink sockets, but it seems all of the examples I find are for the 2.6 kernel.

What I'm trying to find is how to send information from a kernel module to user mode application and vice-versa using Netlink sockets?

Any help would be greatly appreciated. Thanks!

0decimal0
  • 3,657
  • 1
  • 20
  • 37
Zac Reynolds
  • 113
  • 2
  • 6
  • Did you find the implementation dependent on the kernel version? – Devendra D. Chavan Mar 05 '13 at 04:35
  • It seems to be. A lot changed from 2.6 to the 3.x for kernel code in general. Specific to Netlink from what I've found the actual way you create a socket in kernel mode has changed and I cannot find out how to do it now. – Zac Reynolds Mar 05 '13 at 18:46
  • A bit of "generic" kernel code uses netlink sockets; examples are http://lxr.free-electrons.com/source/drivers/scsi/scsi_transport_iscsi.c (the ISCSI transport layer - look for the use of the `nls` variable), or http://lxr.free-electrons.com/source/kernel/audit.c (the kernel C2 audit service - same for `audit_sock`), those seem to give a good indication of how the netlink sockets are used. – FrankH. Mar 07 '13 at 13:56
  • Also, can you give an example of how your 2.6.x-compatible (which .x - 2.6.1 and 2.6.38 are worlds/years apart) code looks like ? Might be easier to start with translating a "sample" to the current way of doing netlink. – FrankH. Mar 07 '13 at 14:05
  • I don't have 2.6.x compatible code. I am working on a new system, not updating an old one so I don't really have anything to go off of and it needs to be in the 3.x kernel so I didn't really see a point in writing the 2.6 stuff. Thanks for the examples though, I'll see if I can figure out how it works and write something from them. – Zac Reynolds Mar 08 '13 at 04:59

1 Answers1

7

I've been working on using Netlink sockets in the Kernel as well and ran into the same problems as you.

Using this previous stack overflow answer as a base, I managed to make it work. Between 3.5 and 3.6 the "netlink_kernel_create" function changed...and I'm on 3.8. The following code should work for you.

netlinkKernel.c

#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__);
/* This is for 3.6 kernels and above.
struct netlink_kernel_cfg cfg = {
    .input = hello_nl_recv_msg,
};

nl_sk = netlink_kernel_create(&init_net, NETLINK_USER, &cfg);*/
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);

MODULE_LICENSE("GPL");

netlinkUser.c

#include <sys/socket.h>
#include <linux/netlink.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.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));
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", (char *)NLMSG_DATA(nlh));
close(sock_fd);
}

Makefile (for netlinkKernel.c)

KBUILD_CFLAGS += -w

obj-m += netlinkKernel.o

all:
    make -w -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
// Make sure the indentations before these "make" lines is a tab
clean:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

Just run "make", then "gcc netlinkUser.c -o netlinkUser", then "sudo insmod netlinkKernel.ko", then when you run "./netlinkUser" you should see that a message was sent to the kernel module and a reply was received by the user space application. Running "dmesg" in the terminal after you will see the debug messages printed by the kernel module.

If you have anymore questions, let me know, I'm knee deep in this right now with my own project.

Community
  • 1
  • 1
Javid Pack
  • 452
  • 5
  • 14