netlink 学习笔记 3.8.13内核

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

3. 释放skb

  发送的skb不需要内核模块去释放,也不能释放,否则会崩溃,因为不能保证netlink_unicast返回不能保证用户层已经接受到消息。内核会处理skb的释放,所以不会出现内存泄露问题

  参考:http://stackoverflow.com/questions/10138848/kernel-crash-when-trying-to-free-the-skb-with-nlmsg-freeskb-out


参考链接:

  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\n");
		return 0;
	}
	printk(KERN_INFO"Kernel sending hello to client %d | portid %d\n", 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\n",
		   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\n", ((int *)(nl_sk+1))[3] & 0x1 ? "TRUE" : "FALSE");
	if(netlink_unicast(nl_sk, skb, pid, 0) < 0)
	{
		printk(KERN_ERR"Failed to unicast skb\n");
		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\n");
		return;

	}
	nlh = (struct nlmsghdr *)skb->data;
	printk(KERN_INFO"%s:received message from %d|%d|%d|%d: %s\n", __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\n",
		   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\n");
// 		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\n");
	}
}

static int __init my_module_init(void)
{
	printk(KERN_INFO"Initializing Netlink Socket\n");
	netlink_test();
	return 0;
}

static void __exit my_module_exit(void)
{
	printk(KERN_INFO"Goodbye\n");
	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\n", 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);
}

  

 

posted @ 2013-07-22 23:09  D3猎人  阅读(4221)  评论(3编辑  收藏  举报