Netlink Sockets in C using the 3.X linux kernel
Asked Answered
G

1

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!

Gaullism answered 5/3, 2013 at 4:32 Comment(5)
Did you find the implementation dependent on the kernel version?Blower
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.Gaullism
A bit of "generic" kernel code uses netlink sockets; examples are lxr.free-electrons.com/source/drivers/scsi/… (the ISCSI transport layer - look for the use of the nls variable), or 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.Longe
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.Longe
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.Gaullism
M
8

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.

Muckworm answered 12/7, 2013 at 2:38 Comment(2)
What if I wanted to send a struct instead of a char? Should I copy_to_user?Locklear
Hijavid i need your help on this, #20139445 can you please helpScrophulariaceous

© 2022 - 2024 — McMap. All rights reserved.