zoukankan      html  css  js  c++  java
  • 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
    ");
        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的释放,所以不会出现内存泄露问题

      参考: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
    ");
    		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);
    }
    

      

  • 相关阅读:
    使用IDEA整合SSM框架
    宏任务与微任务
    setTimeout的实现及其问题
    JS的闭合(Closure)
    this详解
    JS的作用域和作用域链
    JS的执行上下文
    JS内存机制
    抽象工厂模式(c++实现)
    迭代器模式(c++实现)
  • 原文地址:https://www.cnblogs.com/D3Hunter/p/3207670.html
Copyright © 2011-2022 走看看