网上有很多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);
}