网上有很多netlink的教程,但多针对2.6.*的内核,3.8.13内核的netlink API做了很多改动,但总体上差不多
学习netlink除了看别人的教程,感觉要写出个能跑的程序还得自己去读内核代码,就比如netlink_kernel_create这个函数,各版本间有很大不同,如2.6.18和2.6.34都不同,教程上的代码只能作参考
下面主要写一下3.8.13内核相比2.6.*内核在使用netlink上的不同,其他的请参考教程链接。
PS:代码是在虚拟机里写的,不知道怎么拷出来,所以下面的代码是重新手写的,并不完整
PPS:经过这次学习也发现,学内核编程真得在虚拟机里面学,不然时间都花在开关机上面了,另外最好是字符模式,减少开机时间
1. 创建内核sock代码
struct netlink_kernel_cfg cfg = { .input = nl_data_ready,//该函数原型可参考内核代码,其他参数默认即可,可参考内核中的调用 }; nl_sk = netlink_kernel_create(&init_net, NETLINK_TEST, &cfg);// init_net是内核定义的变量,貌似是不建议使用的,测试足够了
2. 内核端发送消息
参考链接里的代码有使用nl_data_ready内接收的skb来发送消息的,测试中发现这个skb不能用来发送(内核直接崩掉),猜测是因为有些变量设置的值不适合发送。
我曾怀疑过是因为不能使用接收的skb来发送,就重新申请了一个skb,然后将接收的skb拷过来,修改参数(portid)发送,仍然崩溃。
教程里的代码在nlmsg_put还设置了skb的一些参数,比如pid什么的,3.8.13内核中不需要设置,默认即可
struct sk_buf *skb; struct nlmsghdr *nlh; // 创建skb skb = nlmsg_new(MAX_PAYLOAD, GFP_ATOMIC); if(!skb) { printk(KERN_ERR"FAILED TO ALLOC SKB "); return; } // 看了下内核的代码,下面的函数不是直观上的put到什么列表里,而是针对nlh的做些初始化工作 nlh = nlmsg_put(skb, 0, 0, 0, MAX_PAYLOAD, 0); memcpy(NLMSG_DATA(nlh), "AA", sizeof("AA"));
// 最后一个参数其实没什么用,因为内核会检测nl_sk是否为内核sock,是的话不会使用该参数 if(netlink_unicast(nl_sk, skb, pid, MSG_DONTWAIT) < 0) { printk(KERN_ERR"FAILED TO send SKB "); return; }
3. 释放skb
发送的skb不需要内核模块去释放,也不能释放,否则会崩溃,因为不能保证netlink_unicast返回不能保证用户层已经接受到消息。内核会处理skb的释放,所以不会出现内存泄露问题
参考链接:
1. http://www.linuxjournal.com/article/7356?page=0,0
2. http://lxr.free-electrons.com/
3. http://blog.csdn.net/RICH_BABA/article/details/5920036
4. http://www.linuxfoundation.org/collaborate/workgroups/networking/generic_netlink_howto
----------------------------------------------------------------------------------------------------------------
下面是编译内核模块的方法,Makefile如下:
obj-m = foo.o# 内核模块对应obj列表,对应源文件为foo.c KVERSION = $(shell uname -r) all: make -C /lib/modules/$(KVERSION)/build M=$(PWD) modules clean: make -C /lib/modules/$(KVERSION)/build M=$(PWD) clean
使用内核模块的命令主要是insmod, lsmod, rmmod,从名称上很容易看出作用:
参考链接:
1. http://www.cyberciti.biz/tips/compiling-linux-kernel-module.html
----------------------------------------------------------------------------------------------------------------
kernel代码如下:
#include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/netlink.h> #include <net/sock.h> #include <net/net_namespace.h> #define NETLINK_TEST 17 #define MAX_PAYLOAD 1024 MODULE_LICENSE("GPL"); static struct sock *nl_sk = NULL; int netlinkSayHello(int pid) { struct sk_buff *skb; struct nlmsghdr *nlh; skb = nlmsg_new(MAX_PAYLOAD, GFP_ATOMIC); if(!skb) { printk(KERN_ERR"Failed to alloc skb "); return 0; } printk(KERN_INFO"Kernel sending hello to client %d | portid %d ", pid, 10); // put into skb nlh = nlmsg_put(skb, 0, 0, 0, MAX_PAYLOAD, 0); printk(KERN_INFO"In Sent Msg type|len|flags|pid|seq %d|%d|%d|%d|%d ", nlh->nlmsg_type, nlh->nlmsg_len, nlh->nlmsg_flags, nlh->nlmsg_pid, nlh->nlmsg_seq); // below line is meaningless // NETLINK_CB(skb).portid = 10;// from kernel memcpy(NLMSG_DATA(nlh), "Hello Client", sizeof("Hello Client")); printk(KERN_INFO"sk is kernel %s ", ((int *)(nl_sk+1))[3] & 0x1 ? "TRUE" : "FALSE"); if(netlink_unicast(nl_sk, skb, pid, 0) < 0) { printk(KERN_ERR"Failed to unicast skb "); return 0; } // free message // nlmsg_free(skb); return 1; } static void nl_data_ready(struct sk_buff *skb) { int pid; struct nlmsghdr *nlh = NULL; if(skb == NULL) { printk(KERN_INFO"skb is NULL "); return; } nlh = (struct nlmsghdr *)skb->data; printk(KERN_INFO"%s:received message from %d|%d|%d|%d: %s ", __FUNCTION__, nlh->nlmsg_pid, NETLINK_CB(skb).portid, nlmsg_total_size(0), skb->len, (char *)NLMSG_DATA(nlh)); // print info of nlh printk(KERN_INFO"In Recved Msg type|len|flags|pid|seq %d|%d|%d|%d|%d ", nlh->nlmsg_type, nlh->nlmsg_len, nlh->nlmsg_flags, nlh->nlmsg_pid, nlh->nlmsg_seq); pid = nlh->nlmsg_pid; // NETLINK_CB(skb).groups = 0; // NETLINK_CB(skb).pid = 0; // from kernel // NETLINK_CB(skb).des_pid = pid; // to pid // NETLINK_CB(skb).dest_groups = 0; // put message into skb // nlmsg_put(skb, 0, 0, 0, MAX_PAYLOAD, 0); // NETLINK_CB(skb).portid = 0; // from kernel // NETLINK_CB(skb).dst_group = 0; //nlh->nlmsg_pid = 0;// from kernel //strcpy(NLMSG_DATA(nlh), "Hello Client"); // newskb = alloc_skb(NLMSG_SPACE(skb->len), GFP_ATOMIC); // if(!newskb) // { // printk(KERN_ERR"Failed to alloc new skb "); // return; // } // newnlh = nlmsg_put(newskb, 0, 0, 0, skb->len, 0); // // memcpy(newskb, skb, skb->len); // // memset(newskb, 0, skb->len); // memcpy(NLMSG_DATA(newnlh), skb, skb->len); // // newnlh->nlmsg_pid = 0;// from kernel // netlink_unicast(nl_sk, newskb, pid, MSG_DONTWAIT); netlinkSayHello(nlh->nlmsg_pid); } static void netlink_test(void) { struct netlink_kernel_cfg cfg = { .input = nl_data_ready, }; nl_sk = netlink_kernel_create(&init_net, NETLINK_TEST, &cfg); if(!nl_sk) { printk(KERN_ERR"Failed to create nerlink socket "); } } static int __init my_module_init(void) { printk(KERN_INFO"Initializing Netlink Socket "); netlink_test(); return 0; } static void __exit my_module_exit(void) { printk(KERN_INFO"Goodbye "); netlink_kernel_release(nl_sk); } module_init(my_module_init); module_exit(my_module_exit);
client代码:
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <linux/netlink.h> #define NETLINK_TEST 17 #define MAX_PAYLOAD 1024 struct sockaddr_nl src_addr, dest_addr; struct msghdr msg; struct nlmsghdr *nlh = NULL; struct iovec iov; int sock_fd; void main() { sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_TEST); memset(&src_addr, 0, sizeof(src_addr)); src_addr.nl_family = AF_NETLINK; src_addr.nl_pid = getpid(); src_addr.nl_groups = 0; bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr)); memset(&dest_addr, 0, sizeof(dest_addr)); dest_addr.nl_family = AF_NETLINK; dest_addr.nl_pid = 0; // send to kernel dest_addr.nl_groups = 0; nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD)); nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD); nlh->nlmsg_pid = getpid(); nlh->nlmsg_flags = 0; strcpy(NLMSG_DATA(nlh), "Hello Kernel"); 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; sendmsg(sock_fd, &msg, 0); // send message to kernel // read message from kernel memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD)); int msgLen = recvmsg(sock_fd, &msg, 0); printf("Received mesage payload: %d|%s ", iov.iov_len, (char *)NLMSG_DATA(nlh)); // int fd = open("a.txt", O_WRONLY|O_CREAT); // write(fd, NLMSG_DATA(nlh), msgLen); // close socket close(sock_fd); }