Monday 13 February 2017

netlink sockets examples in linux

Kernel side code:

//kernel module: netlink-exam-kern.c
#include <linux/module.h>
#include <linux/netlink.h>
#include <linux/sched.h>
#include <net/sock.h>
#include <linux/proc_fs.h>
#include <linux/version.h>
#define BUF_SIZE 16384
static struct sock *netlink_exam_sock;
static unsigned char buffer[BUF_SIZE];
static unsigned int buffer_tail = 0;
#define NETLINK_TEST 21
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
#define sk_sleep(sk) sk->sk_sleep
#endif
#define RCV_SKB_FAIL(err) do { netlink_ack(skb, nlh, (err)); return; } while (0)
static void recv_handler(struct sk_buff *skb)
{
struct nlmsghdr *nlhdr = (struct nlmsghdr *)skb->data;
size_t len = 0;
if (nlhdr->nlmsg_len < sizeof(struct nlmsghdr)) {
printk("Corrupt netlink message.\n");
}
len = nlhdr->nlmsg_len - NLMSG_LENGTH(0);
if (len + buffer_tail > BUF_SIZE) {
printk("netlink buffer is full.\n");
}
else {
memcpy(buffer + buffer_tail, NLMSG_DATA(nlhdr), len);
buffer_tail += len;
}
}
static int netlink_exam_readproc(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
int len;
if (off >= buffer_tail) {
* eof = 1;
return 0;
}
else {
len = count;
if (count > PAGE_SIZE) {
len = PAGE_SIZE;
}
if (len > buffer_tail - off) {
len = buffer_tail - off;
}
memcpy(page, buffer + off, len);
*start = page;
return len;
}
}
static int __init netlink_exam_init(void)
{
netlink_exam_sock = netlink_kernel_create(&init_net, NETLINK_TEST, 0, recv_handler, NULL, THIS_MODULE);
if (!netlink_exam_sock) {
printk("Fail to create netlink socket.\n");
return -1;
}
create_proc_read_entry("netlink_exam_buffer", 0444, NULL, netlink_exam_readproc, 0);
return 0;
}
static void __exit netlink_exam_exit(void)
{
wake_up(sk_sleep(netlink_exam_sock));
sock_release(netlink_exam_sock->sk_socket);
remove_proc_entry("netlink_exam_buffer", NULL);
}
module_init(netlink_exam_init);
module_exit(netlink_exam_exit);
MODULE_LICENSE("GPL");

Make File:

mname :=file name
$(mname)-objs := netlink-exam-kern.o
obj-m := $(mname).o
KERNELDIR := /lib/modules/`uname -r`/build
all:
$(MAKE) -C $(KERNELDIR) M=`pwd` modules
clean:
$(MAKE) -C $(KERNELDIR) M=`pwd` clean

User side Code:

Send :
//application sender: netlink-exam-user-send.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#define MAX_MSGSIZE 1024
#define NETLINK_TEST 21
int main(int argc, char * argv[])
{
FILE * fp;
struct sockaddr_nl saddr, daddr;
struct nlmsghdr *nlhdr = NULL;
struct msghdr msg;
struct iovec iov;
int sd;
char text_line[MAX_MSGSIZE];
int ret = -1;
if (argc < 2) {
printf("Usage: %s atextfilename\n", argv[0]);
exit(1);
}
if ((fp = fopen(argv[1], "r")) == NULL) {
printf("File %s dosen't exist.\n", argv[1]);
exit(1);
}
sd = socket(AF_NETLINK, SOCK_RAW, NETLINK_TEST);
if (sd == -1) {
printf("Cannot create netlink\n");
exit(1);
}
memset(&saddr, 0, sizeof(saddr));
memset(&daddr, 0, sizeof(daddr));
saddr.nl_family = AF_NETLINK;
saddr.nl_pid = getpid();
saddr.nl_groups = 11;
bind(sd, (struct sockaddr*)&saddr, sizeof(saddr));
daddr.nl_family = AF_NETLINK;
daddr.nl_pid = 0;
daddr.nl_groups = 1;
nlhdr = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_MSGSIZE));
while (fgets(text_line, MAX_MSGSIZE, fp)) {
memcpy(NLMSG_DATA(nlhdr), text_line, strlen(text_line));
memset(&msg, 0 ,sizeof(struct msghdr));
nlhdr->nlmsg_len = NLMSG_LENGTH(strlen(text_line));
nlhdr->nlmsg_pid = getpid(); /* self pid */
nlhdr->nlmsg_flags = 0;
iov.iov_base = (void *)nlhdr;
iov.iov_len = nlhdr->nlmsg_len;
msg.msg_name = (void *)&daddr;
msg.msg_namelen = sizeof(daddr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
ret = sendmsg(sd, &msg, 0);
if (ret == -1) {
perror("sendmsg error:");
}
}
close(sd);
}

Receive :

//application receiver: netlink-exam-user-recv.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <string.h>
#include <stdlib.h>
#define MAX_MSGSIZE 1024
#define NETLINK_TEST 21
int main(void)
{
struct sockaddr_nl saddr, daddr;
struct nlmsghdr *nlhdr = NULL;
struct msghdr msg;
struct iovec iov;
int sd;
int ret = 1;
sd = socket(AF_NETLINK, SOCK_RAW, NETLINK_TEST);
if (sd == -1) {
printf("cannot create netlink\n");
exit(-1);
}
memset(&saddr, 0, sizeof(saddr));
memset(&daddr, 0, sizeof(daddr));
saddr.nl_family = AF_NETLINK;
saddr.nl_pid = getpid();
saddr.nl_groups = 11;
bind(sd, (struct sockaddr*)&saddr, sizeof(saddr));
nlhdr = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_MSGSIZE));
while (1) {
memset(nlhdr, 0, NLMSG_SPACE(MAX_MSGSIZE));
iov.iov_base = (void *)nlhdr;
iov.iov_len = NLMSG_SPACE(MAX_MSGSIZE);
msg.msg_name = (void *)&daddr;
msg.msg_namelen = sizeof(daddr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
ret = recvmsg(sd, &msg, 0);
if (ret == 0) {
printf("Exit.\n");
exit(0);
}
else if (ret == -1) {
perror("recvmsg:");
exit(1);
}
printf("%s", (char *)NLMSG_DATA(nlhdr));
}
close(sd);
}
Make file:

.PHONY: clean
all:
gcc netlink-exam-user-recv.c -o netlink-exam-user-recv
gcc netlink-exam-user-send.c -o netlink-exam-user-send
clean:
rm netlink-exam-user-recv netlink-exam-user-send