通过netlink实现内核模块和应用层通信

netlink主要适用于内核像应用层发送数据使用,应用层通过阻塞调用接受数据,而应用层像内核层发送数据则主要用IOCTL机制配合copy_from_user和copy_to_user函数来使用。如果是相互交互机制,则使用消息机制和内存共享来达成。

netlink机制内核态的接受函数仅调用一次吗?(就是在内核态创建sock的时候使用了该函数。)

贴上代码,方便以后查阅。

skb常用操作函数,比较容易弄混

skb_put : skb->tail += len, skb->len += len
skb_pull: skb->data += len, skb->len -= len
skb_push: skb->data -= len, skb->len += len


内核版本linux2.6.38,编译环境gcc 4.4.4,centos6.0

内核模块代码:


  1. /* 
  2.  * author: hoi0714@163.com 
  3.  * date  : 2011-10-29 
  4.  */  
  5. #include <linux/module.h>  
  6. #include <linux/netlink.h>  
  7. #include <linux/sched.h>  
  8. #include <net/sock.h>  
  9. #include <linux/proc_fs.h>  
  10. #include <linux/netfilter.h>  
  11. #include <linux/netfilter_ipv4.h>  
  12. #include <linux/ip.h>  
  13. #include <linux/tcp.h>  
  14. #include <linux/icmp.h>  
  15. #include <linux/udp.h>  
  16.   
  17. #define NETLINK_TEST 30  
  18.   
  19. /* 调试信息 */  
  20. #define LOGMSG(fmt, arg...) \  
  21. do{ \  
  22.     printk("[func:%s,line:%d]: "fmt, __FUNCTION__, __LINE__, ##arg); \  
  23. }while(0)  
  24. /* 错误信息 */  
  25. #define LOGERR(fmt, arg...) \  
  26. do{ \  
  27.     printk("[func:%s,line:%d]: "fmt, __FUNCTION__, __LINE__, ##arg); \  
  28. }while(0)  
  29. /* 断言 */  
  30. #define ASSERT(expr) \  
  31. if (unlikely(!(expr))) { \  
  32.     printk("Assertion failed! %s,%s,%s,line=%d\n", \  
  33.     #expr, __FILE__, __func__, __LINE__); \  
  34. }  
  35. /* 消息最大值 */  
  36. #define MAX_MSG_LEN 1024  
  37. enum{  
  38.     NLMSG_TYPE_NONE = 0,  
  39.     NLMSG_TYPE_SETPID,  /* 设置PID */  
  40.     NLMSG_TYPE_KERNEL,  /* 消息来自内核 */  
  41.     NLMSG_TYPE_APP,     /* 消息来自应用层 */  
  42. };  
  43. struct nlmsg{  
  44.     int type;               /* 消息类型 */  
  45.     int len;                /* 消息长度,包括头部 */  
  46.     char msg[MAX_MSG_LEN];  /* 消息正文 */  
  47. };  
  48. /* netlink socket */  
  49. static struct sock *g_nl_sk = NULL;  
  50. static int g_nlpid = -1;    /* 应用层接收程序PID */  
  51.   
  52. /* 
  53.  * 发送整个从ip头开始的skb数据到应用层 
  54.  *  
  55.  * param[in]: sk, skb发送目的socket 
  56.  * param[in]: skb, 待发送的skb 
  57.  * return -1, 失败; 0, 成功 
  58.  * */  
  59. int nl_sendskb(struct sock *sk, struct sk_buff *skb)  
  60. {  
  61.     struct iphdr *iph = NULL;  
  62.     struct nlmsghdr *nlh = NULL;  
  63.     struct sk_buff *nl_skb = NULL;  
  64.   
  65.     int skb_len = 0;  
  66.   
  67.     ASSERT(skb != NULL);  
  68.     ASSERT(sk != NULL);  
  69.     if(g_nlpid < 0)  
  70.         return 0;  
  71.   
  72.     iph = ip_hdr(skb);  
  73.     skb_len = iph->tot_len;  
  74.     /* NLMSG_SPACE: sizeof(struct nlmsghdr) + len按4字节对齐 */  
  75.     nl_skb = alloc_skb(NLMSG_SPACE(skb_len), GFP_ATOMIC);  
  76.     if(!nl_skb)  
  77.     {  
  78.         LOGERR("nl_skb == NULL, failed!\n");  
  79.         return -1;  
  80.     }  
  81.     /* 
  82.      * static inline struct nlmsghdr *nlmsg_put(struct sk_buff *skb, u32 pid, u32 seq, 
  83.      *               int type, int payload, int flags); 
  84.      * 设置skb->tail指针指向skb->data + sizeof(struct nlmsghdr) + payload 
  85.      * skb->len = sizeof(struct nlmsghdr) + payload  
  86.      */  
  87.     nlh = nlmsg_put(nl_skb, 0, 0, 0, NLMSG_SPACE(skb_len) - sizeof(struct nlmsghdr), 0);   
  88.     NETLINK_CB(nl_skb).pid = 0; /* 0代表数据来自内核 */  
  89.     memcpy(NLMSG_DATA(nlh), (char *)iph, htons(iph->tot_len));  
  90.     /* 将数据发送给进程号22345的进程 */  
  91.     return netlink_unicast(sk, nl_skb, g_nlpid , MSG_DONTWAIT);  
  92. }  
  93. /* 
  94.  * 发送字符串到应用层 
  95.  * 
  96.  * param[in]: sk, 数据发往的socket 
  97.  * param[in]: pmsg, 待发送字符串 
  98.  * param[in]: msglen, 待发送字符串长度 
  99.  *  
  100.  * return: -1, 失败; 0, 成功 
  101.  * */  
  102. int nl_sendmsg(struct sock *sk, struct nlmsg *pmsg)  
  103. {  
  104.     struct nlmsghdr *nlh = NULL;  
  105.     struct sk_buff *nl_skb = NULL;  
  106.     int msglen = pmsg->len;  
  107.   
  108.     ASSERT(pmsg != NULL);  
  109.     ASSERT(sk != NULL);  
  110.   
  111.     if(g_nlpid < 0)  
  112.         return 0;  
  113.     nl_skb = alloc_skb(NLMSG_SPACE(msglen), GFP_ATOMIC);  
  114.     if(!nl_skb)  
  115.     {  
  116.         LOGERR("nl_skb == NULL, msglen = %d, failed!\n", msglen);  
  117.         return -1;  
  118.     }  
  119.   
  120.     nlh = nlmsg_put(nl_skb, 0, 0, 0,   
  121.                     NLMSG_SPACE(msglen) - NLMSG_HDRLEN, 0);   
  122.     NETLINK_CB(nl_skb).pid = 0;  
  123.     memcpy(NLMSG_DATA(nlh), pmsg, msglen);  
  124.   
  125.     return netlink_unicast(sk, nl_skb, g_nlpid , MSG_DONTWAIT);  
  126. }  
  127. /*  
  128.  * 从应用层接收数据, netlink_kernel_create注册的回调 
  129.  * param[in]: skb, 包含netlink数据的skb 
  130.  * 
  131.  * skb常用操作函数 
  132.  * skb_put : skb->tail += len, skb->len += len 
  133.  * skb_pull: skb->data += len, skb->len -= len 
  134.  * skb_push: skb->data -= len, skb->len += len 
  135.  */  
  136. static void nl_recvmsg(struct sk_buff *skb)  
  137. {  
  138.     struct nlmsg *pmsg = NULL;  
  139.     struct nlmsghdr *nlh = NULL;  
  140.     uint32_t rlen = 0;  
  141.       
  142.     while(skb->len >= NLMSG_SPACE(0))  
  143.     {  
  144.         nlh = nlmsg_hdr(skb);  
  145.         if(nlh->nlmsg_len < sizeof(*nlh) || skb->len < nlh->nlmsg_len)  
  146.             return;  
  147.         rlen = NLMSG_ALIGN(nlh->nlmsg_len);  
  148.         if(rlen > skb->len)  
  149.             rlen = skb->len;  
  150.         pmsg = (struct nlmsg*)NLMSG_DATA(nlh);  
  151.         switch(pmsg->type)  
  152.         {  
  153.         case NLMSG_TYPE_SETPID:  
  154.             g_nlpid = nlh->nlmsg_pid;  
  155.             LOGMSG("pid: %d\n", g_nlpid);  
  156.             LOGMSG("msg: %s\n", pmsg->msg);  
  157.             break;  
  158.         case NLMSG_TYPE_KERNEL:  
  159.             break;  
  160.         case NLMSG_TYPE_APP:  
  161.             break;  
  162.         }  
  163.         /* 获取下一条netlink消息 */  
  164.         skb_pull(skb, rlen);  
  165.     }  
  166. }  
  167.   
  168. /*  
  169.  * netfilter PRE_ROUTING钩子 
  170.  * */  
  171. unsigned int pre_routing_hook(unsigned int hooknum,   
  172.                            struct sk_buff *skb,   
  173.                            const struct net_device *in,  
  174.                            const struct net_device *out,  
  175.                            int (*okfn)(struct sk_buff *))  
  176. {  
  177.     char *psend = "msg for kernel";  
  178.     struct nlmsg msg;  
  179.     int ret = 0;  
  180.   
  181.     msg.type = NLMSG_TYPE_KERNEL;  
  182.     msg.len = strlen(psend) + offsetof(struct nlmsg, msg) + 1;  
  183.     memcpy(msg.msg, psend, msg.len);  
  184.     //ret = nl_sendskb(g_nl_sk, skb);  
  185.     ret = nl_sendmsg(g_nl_sk, &msg);  
  186.     //LOGMSG("ok\n");  
  187.     return NF_ACCEPT;  
  188. }  
  189.   
  190. static struct nf_hook_ops local_in_ops __read_mostly = {  
  191.     .hook = pre_routing_hook,  
  192.     .owner = THIS_MODULE,  
  193.     .pf = PF_INET,  
  194.     .hooknum = NF_INET_PRE_ROUTING,  
  195.     .priority = NF_IP_PRI_FIRST  
  196. };  
  197.   
  198. static int __init nl_init(void)  
  199. {  
  200.     int ret = 0;  
  201.     /*  
  202.      * struct sock *netlink_kernel_create(struct net *net, int unit, unsigned int groups, 
  203.      *                                    void (*input)(struct sk_buff *skb), 
  204.      *                                    struct mutex *cb_mutex, struct module *module) 
  205.      */  
  206.     g_nl_sk = netlink_kernel_create(&init_net, NETLINK_TEST, 0, nl_recvmsg, NULL, THIS_MODULE);  
  207.     if (!g_nl_sk) {  
  208.         LOGERR("Fail to create netlink socket.\n");  
  209.         return -1;  
  210.     }  
  211.   
  212.     ret = nf_register_hook(&local_in_ops);  
  213.     if(ret < 0)  
  214.     {  
  215.         LOGMSG("nf_register_hook failed!\n");  
  216.         goto sock_release;  
  217.     }  
  218.     LOGMSG("ok!\n");  
  219.     return 0;  
  220.   
  221. sock_release:  
  222.     if(g_nl_sk)  
  223.         sock_release(g_nl_sk->sk_socket);  
  224.     return -1;  
  225. }  
  226.   
  227. static void __exit nl_exit(void)  
  228. {  
  229.     synchronize_sched();  
  230.     if(g_nl_sk)  
  231.         sock_release(g_nl_sk->sk_socket);  
  232.     nf_unregister_hook(&local_in_ops);  
  233.     LOGMSG("ok!\n");  
  234. }  
  235.   
  236. module_init(nl_init);  
  237. module_exit(nl_exit);  
  238. MODULE_LICENSE("GPL");  
  239. MODULE_AUTHOR("hoi0714@163.com");  


内核模块Makefile

  1. module := netlink  
  2.   
  3. obj-m := $(module).o  
  4.   
  5. #$(module)-objs :=  $(module).o  
  6.   
  7. KDIR=/root/kernel_new  
  8. KHDR=/root/kernel_new/include  
  9. EXTRA_CFLAGS += $(FLAG) -I$(KHDR) -O2 -D__KERNEL__ -DMODULE $(INCLUDE) -DEXPORT_SYMTAB  
  10. CPPFLAGS += $(FLAG) -I$(KHDR)  
  11.   
  12. OUTDIR =  
  13. TARGET = netlink.ko  
  14. TARGETDIR = /lib/modules/  
  15.   
  16. all:  
  17.     make -C $(KDIR) M=$(PWD) modules  
  18.   
  19. clean:  
  20.     make -C $(KDIR) M=$(PWD) clean   
  21.   
  22.   
  23. install: all  
  24.     install --verbose --mode=0755 $(OUTDIR)$(TARGET) $(INSTALLROOT)$(TARGETDIR)  


应用层代码:

  1. /* 
  2.  * author: hoi0714@163.com 
  3.  * date  : 2011-10-29 
  4.  */  
  5. #include <sys/stat.h>     
  6. #include <unistd.h>     
  7. #include <stdio.h>     
  8. #include <stdlib.h>     
  9. #include <sys/socket.h>     
  10. #include <sys/types.h>     
  11. #include <string.h>     
  12. #include <asm/types.h>     
  13. #include <linux/netlink.h>     
  14. #include <linux/socket.h>     
  15. #include <stddef.h>  
  16. #include <errno.h>     
  17.   
  18. #define FREE_INIT(ptr) do{ \  
  19.     free(ptr); \  
  20.     ptr = NULL; \  
  21. }while(0)  
  22.   
  23. /* 消息最大值 */  
  24. #define MAX_MSG_LEN 1024  
  25. enum{  
  26.     NLMSG_TYPE_NONE = 0,  
  27.     NLMSG_TYPE_SETPID,  /* 设置PID */  
  28.     NLMSG_TYPE_KERNEL,  /* 消息来自内核 */  
  29.     NLMSG_TYPE_APP,     /* 消息来自应用层 */  
  30. };  
  31. struct nlmsg{  
  32.     int type;               /* 消息类型 */  
  33.     int len;                /* 消息长度,包括头部 */  
  34.     char msg[MAX_MSG_LEN];  /* 消息正文 */  
  35. };  
  36.   
  37. #define NETLINK_TEST 30     
  38.   
  39. /* 
  40.  * 打开netlink 
  41.  * return: 0, 成功; -1, 失败 
  42.  * */  
  43. int netlink_open(void)  
  44. {  
  45.     struct sockaddr_nl saddr;  
  46.     int sockfd = -1, ret = 0;  
  47.       
  48.     sockfd = socket(PF_NETLINK, SOCK_RAW, NETLINK_TEST);    
  49.     if(sockfd < -1){    
  50.         perror("create socket!\n");  
  51.         return -1;    
  52.     }    
  53.       
  54.     memset(&saddr, 0, sizeof(saddr));    
  55.     saddr.nl_family = PF_NETLINK;     
  56.     saddr.nl_pid = getpid();    // self pid      
  57.     saddr.nl_groups = 0;        // multi cast   
  58.       
  59.     ret = bind(sockfd, (struct sockaddr*)&saddr, sizeof(saddr));    
  60.     if(ret < 0){    
  61.         perror("bind failed!\n");    
  62.         close(sockfd);    
  63.         return -1;    
  64.     }   
  65.   
  66.     return sockfd;  
  67. }  
  68. /* 
  69.  * 发送信息 
  70.  * param[in]: sockfd 
  71.  * param[in]: pmsg, 待发送信息 
  72.  *  
  73.  * return: 0, 发送成功; -1: 发送失败 
  74.  * */  
  75. int netlink_send(int sockfd, struct nlmsg *pmsg)  
  76. {    
  77.     struct msghdr msg;  
  78.     struct iovec iov;  
  79.     struct nlmsghdr *nlh = NULL;  
  80.       
  81.     int msglen = pmsg->len;  
  82.     int totlen = NLMSG_SPACE(pmsg->len);  
  83.     int ret = 0;  
  84.       
  85.     nlh = malloc(totlen);  
  86.     if(!nlh)  
  87.     {  
  88.         fprintf(stderr, "malloc failed!\n");  
  89.         return -1;  
  90.     }  
  91.     nlh->nlmsg_len = totlen;  
  92.     nlh->nlmsg_flags = 0;  
  93.     nlh->nlmsg_pid = getpid();  
  94.       
  95.     iov.iov_base = (void *)nlh;    
  96.     iov.iov_len = nlh->nlmsg_len;    
  97.     
  98.     memset(&msg, 0, sizeof(msg));    
  99.     msg.msg_iov = &iov;    
  100.     msg.msg_iovlen = 1;   
  101.       
  102.       
  103.     memcpy(NLMSG_DATA(nlh), pmsg, msglen);  
  104.     ret = sendmsg(sockfd, &msg, 0);  
  105.     if(ret < 0)  
  106.     {  
  107.         fprintf(stderr, "sendmsg failed!\n");  
  108.         FREE_INIT(nlh);  
  109.         return -1;  
  110.     }  
  111.     return 0;  
  112.       
  113. }  
  114. /* 
  115.  * 接收信息 
  116.  * param[in]: sockfd 
  117.  * param[out]: pmsg 
  118.  *  
  119.  * return 0, 成功; -1, 失败 
  120.  * */  
  121. int netlink_recv(int sockfd, struct nlmsg *pmsg)  
  122. {  
  123.     struct msghdr msg;  
  124.     struct iovec iov;  
  125.     struct nlmsghdr *nlh = NULL;  
  126.       
  127.     int msglen = sizeof(*pmsg);  
  128.     int totlen = NLMSG_SPACE(sizeof(*pmsg));  
  129.     int ret = 0;  
  130.       
  131.     nlh = malloc(totlen);  
  132.     if(!nlh)  
  133.     {  
  134.         fprintf(stderr, "malloc failed!\n");  
  135.         return -1;  
  136.     }  
  137.       
  138.     iov.iov_base = (void *)nlh;    
  139.     iov.iov_len = totlen;    
  140.     
  141.     memset(&msg, 0, sizeof(msg));    
  142.     msg.msg_iov = &iov;    
  143.     msg.msg_iovlen = 1;   
  144.       
  145.     memcpy(NLMSG_DATA(nlh), pmsg, msglen);  
  146.     ret = recvmsg(sockfd, &msg, 0);  
  147.     if(ret < 0)  
  148.     {  
  149.         fprintf(stderr, "recvmsg failed!\n");  
  150.         FREE_INIT(nlh);  
  151.         return -1;  
  152.     }  
  153.     memcpy(pmsg, NLMSG_DATA(nlh), msglen);  
  154.     return 0;  
  155. }  
  156. /*  
  157.  * 关闭netlink 
  158.  * param[in]: sockfd, netlink socket号 
  159.  * */  
  160. void netlink_close(int sockfd)  
  161. {  
  162.     if(sockfd > 0)  
  163.         close(sockfd);  
  164. }  
  165.   
  166. int main(int argc, char* argv[])    
  167. {    
  168.     int sockfd = -1;  
  169.     int ret = 0;  
  170.   
  171.     struct nlmsg msg;  
  172.   
  173.     char *psend = "msg from app!";  
  174.       
  175.     memset(&msg, 0, sizeof(msg));  
  176.     /* 创建netlink socket */  
  177.     sockfd = netlink_open();  
  178.     if(sockfd < 0)  
  179.     {  
  180.         fprintf(stderr, "netlink_open failed!\n");  
  181.         return -1;  
  182.     }  
  183.     /* 将进程号通知内核 */  
  184.     msg.type = NLMSG_TYPE_SETPID;  
  185.     msg.len = strlen(psend) + offsetof(struct nlmsg, msg) + 1;  
  186.     memcpy(msg.msg, psend, strlen(psend));  
  187.     ret = netlink_send(sockfd, &msg);  
  188.     if(ret < 0)  
  189.     {  
  190.         fprintf(stderr, "netlink_send failed!\n");  
  191.         return -1;  
  192.     }  
  193.     /* 接收消息 */  
  194.     while(1)  
  195.     {  
  196.         ret = netlink_recv(sockfd, &msg);  
  197.         if(ret < 0)  
  198.         {  
  199.             fprintf(stderr, "netlink_recv failed!\n");  
  200.             return -1;  
  201.         }  
  202.         printf("msg: %s\n", msg.msg);  
  203.     }  
  204.     /* 关闭netlink */  
  205.     netlink_close(sockfd);  
  206.     return 0;    
  207. }   
一、imp2源码 整个源码包含三个文件:imp2_k.c, imp2_u.c和imp2.h. 其中imp2_k.c为内核模块的源代码,imp2_u.c为应用程序,即测试代码,imp2.h为两个源文件都需要引用的头文件。其整体的功能是:注册一种新的netlink协议,并注册一个新的NF hook函数。当有ping包发往当前主机或者经过当前主机转发时,内核向用户发送ping包的源IP和目的IP。各个文件的简单分析见下文。 1. imp2.h 该文件主要是定义了一种新的Netlink协议类型NL_IMP2(31)。新的协议类型的选值不能和当前内核中已经定义的netlink协议类型重复。定义了基于该协议类型的消息类型,内核根据接收到消息的不同类型,进行不同的处理:IMP2_U_PID和IMP2_CLOSE分别为请求和关闭。IMP2_K_MSG代表内核空间发送的消息。 该头文件的源码如下: 2. imp2_k.c 该程序为内核模块程序。其完成的功能如下: (1)创建一种新的Netlink协议NL_IMP2,并注册该协议的回调函数kernel_receive。但用户空间通过建立且协议类型为NL_IMP2的socket套接字并调用sendto,sendmsg函数发送数据时,传送到内核空间的数据由kernel_receive进行处理。该函数主要是记录用户进程的ID,用于随后发送数据的时候指定目的。 (2)在Netfilter的hook点NF_IP_PRE_ROUTING注册hook函数get_icmp,对经过该hook点的ping包进行处理。get_icmp首先判断是否是ping包,如果不是,直接Accept。如果是,则记录该包的源IP和目的IP,然后调用send_to_user,将记录的信息发送给kernel_recieve函数中记录的用户进程ID。 该文件的源码如下: 3. imp2_u.c 该程序为用户空间的测试程序。该程序包括以下功能: (1)生成NL_IMP2协议的socket.然后通过调用sendto发送IMP2_U_PID类型的请求信息给内核。然后等待接受内核发回的信息。记住:仅当有ping包经过内核的NF时,内核才会向用户进程发送信息。 (2)当用户进程通过Ctrl+C来结束该程序时,调用信号处理函数sig_int,向内核发送IMP2_CLOSE的消息,结束socket。 该文件的源码如下: 二、编译和测试 1. 整个源文件编译的Makefile如下: all: gcc -O2 -DMODULE -D__KERNEL__ -W -Wstrict-prototypes -Wmissing-prototypes -isystem /lib/modules/`uname -r`/build/include -c -o imp2_k.o imp2_k.c gcc imp2_u.c -o imp2_u install: insmod imp2_k.o uninstall: rmmod imp2_k clean: rm -f imp2_k.o imp2_u 2. 加载内核模块,并执行测试程序。 #make install #./imp2_u 当没有ping包时,终端一直处于等待输出状态。通过另一台主机(192.168.1.100)向当前主机(192.168.1.101)发送ping包,则终端已经有输出: #./imp2_u [root@localhost imp2]# ./imp2_u src: 192.168.1.100, dest: 192.168.1.101 src: 192.168.1.100, dest: 192.168.1.101 src: 192.168.1.100, dest: 192.168.1.101 src: 192.168.1.100, dest: 192.168.1.101
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值