2.6.24以上内核中netlink使用方法

2024-03-15 05:32

本文主要是介绍2.6.24以上内核中netlink使用方法,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!


http://blog.csdn.net/wangjingfei/article/details/5288460


2.6.24以上内核中netlink使用方法

测试环境:2.6.28

Netlink在2.6内核的不同版本中发生了很大变化,具体请参考(注意其中的版本号不一定确切):

http://blog.csdn.net/sealyao/archive/2009/10/02/4628141.aspx

 

0. 综述

以下程序基本流程如下:

运行netlink内核模块;

运行用户态程序,向内核发送连接消息,通知内核自身进程id;

内核接收用户消息,记录其进程id;

内核向用户进程id发送netlink消息;

用户接收内核发送的netlink消息。

 

1. 内核部分

1.1 相关的数据结构变量:

[cpp] view plain copy
  1. 44 // --------- These are for netlink --------- //  
  2. 45 #define NETLINK_REALNET     26  
  3. 46 struct sock *g_nl_sk = NULL;  
  4. 48 struct sockaddr_nl src_addr, dest_addr;  
  5. 50 struct iovec iov;  
  6. 52 int pid;  
  7. 53 struct msghdr msg;  
  8. 55 // ----------------------------------------- //  
 

 

[cpp] view plain copy
  1. 45 #define NETLINK_REALNET     26  
 

定义协议族。该变量在netlink_kernel_create函数中使用。

在2.6.28内核中netlink定义了20个协议,每个协议使用唯一整数标识。用户程序可以定义任意20个协议以外的协议,用唯一整数标识。

[cpp] view plain copy
  1. 46 struct sock *g_nl_sk = NULL;  

sock数据结构,唯一标识netlink使用的sock,与普通socket编程中sock类似。

[cpp] view plain copy
  1. 48 struct sockaddr_nl src_addr, dest_addr;  

标识netlink sock的源地址和目的地址。

[cpp] view plain copy
  1. 50 struct iovec iov;  

接收发送netlink数据使用的数据结构。

[cpp] view plain copy
  1. 53 struct msghdr msg;  

netlink消息头。

 

1.2 调用过程

1.2.1 创建netlink socket

[cpp] view plain copy
  1. g_nl_sk = netlink_kernel_create(&init_net, NETLINK_REALNET, 0, nl_data_r    eady, NULL, THIS_MODULE);  

1.2.2 实现回调函数nl_data_ready

以下回调函数在netlink接收到完整的NETLINK_REALNET协议的数据包时由系统调用。该函数接收并判断netlink消息,如果第一个字符为H,则保存该消息发出者的进程号,用以向该进程发送数据包;相同,如果为E,则清除与该进程的联系。

[cpp] view plain copy
  1. 185 void nl_data_ready(struct sk_buff *__skb)  
  2. 186 {  
  3. 187     struct sk_buff *skb;  
  4. 188     struct nlmsghdr *nlh;  
  5. 189     char str[100];  
  6. 190   
  7. 191     skb = skb_get (__skb);  
  8. 192   
  9. 193     if(skb->len >= NLMSG_SPACE(0))  
  10. 194     {  
  11. 195         nlh = nlmsg_hdr(skb);  
  12. 196   
  13. 197         memcpy(str, NLMSG_DATA(nlh), sizeof(str));  
  14. 198         //DbgPrint("Message received: %s/n", str);  
  15. 199   
  16. 200         // H stands for Hello message.  
  17. 201         if(str[0] == 'H')  
  18. 202         {  
  19. 203             pid = nlh->nlmsg_pid;  
  20. 204             u_connected = 1;  
  21. 205             //sendnlmsg("Hello reply.");  
  22. 206         }  
  23. 207         // E stands for Exit message  
  24. 208         else if(str[0] == 'E')  
  25. 209         {  
  26. 210             u_connected = 0;  
  27. 211             pid = 0;  
  28. 212         }  
  29. 213         kfree_skb(skb);  
  30. 214     }  
  31. 215 }  
 
[cpp] view plain copy
  1. 191 skb = skb_get (__skb);  

获取实际数据包。

该函数的参数为netlink数据包的首地址,而sk_buff为网络协议栈使用的数据结构,两者存在细微差别。

[cpp] view plain copy
  1. 195 nlh = nlmsg_hdr(skb);  

    获取netlink数据包中netlink header的起始地址。

[cpp] view plain copy
  1. 197 memcpy(str, NLMSG_DATA(nlh), sizeof(str));  

将netlink数据包的数据区拷贝到str中。NLMSG_DATA(nlh)返回数据区地址。相关宏定义参考:

http://blog.csdn.net/wangjingfei/archive/2010/02/04/5288263.aspx

[cpp] view plain copy
  1. 213 kfree_skb(skb);  

释放接收到的消息。

 

1.2.3 向用户进程发送netlink消息

    以下函数的参数为netfilter捕捉到的sk_buff结构的数据包,目的是将该包通过netlink发送到用户态进程。

[cpp] view plain copy
  1. 247 void send_to_user(struct sk_buff *skb)  
  2. 248 {  
  3. 249     struct iphdr *iph;  
  4. 250     struct ethhdr *ehdr;  
  5. 251   
  6. 252     struct nlmsghdr *nlh;  
  7. 253   
  8. 254     struct sk_buff *nl_skb;  
  9. 255   
  10. 256     //DbgPrint("Send packages to user/n");  
  11. 257   
  12. 258     if(skb == NULL)  
  13. 259     {  
  14. 260         return ;  
  15. 261     }  
  16. 262     if(!g_nl_sk)  
  17. 263     {  
  18. 264         return ;  
  19. 265     }  
  20. 266     if(pid == 0)  
  21. 267     {  
  22. 268         return;  
  23. 269     }  
  24. 270   
  25. 271     nl_skb = alloc_skb(NLMSG_SPACE(1514), GFP_ATOMIC);  
  26. 272     //nl_skb = alloc_skb(NLMSG_SPACE(0), GFP_ATOMIC);  
  27. 273     if(nl_skb == NULL)  
  28. 274     {  
  29. 275         // allocate failed.  
  30. 276         return;  
  31. 277     }  
  32. 278   
  33. 279     ehdr = eth_hdr(skb);  
  34. 280     iph = ip_hdr(skb);  
  35. 281   
  36. 282     nlh = nlmsg_put(nl_skb, 0, 0, 0, NLMSG_SPACE(1514) - sizeof(struct nlmsghdr), 0);  
  37. 283     NETLINK_CB(nl_skb).pid = 0;  
  38. 284   
  39. 285     //DbgPrint("Data length: %d, len=%d/n", htons(iph->tot_len) + ETH_HLEN,     NLMSG_SPACE(1514));  
  40. 286   
  41. 287     // Copy data to nlh  
  42. 288     memcpy(NLMSG_DATA(nlh), (char *)ehdr, htons(iph->tot_len) + ETH_HLEN);  
  43. 289   
  44. 290     netlink_unicast(g_nl_sk, nl_skb, pid, MSG_DONTWAIT);  
  45. 291 }  
 
[cpp] view plain copy
  1. 247 void send_to_user(struct sk_buff *skb)  

参数skb为netfilter捕捉到的数据包,不是netlink数据包。这里作为netlink的数据传输。

[cpp] view plain copy
  1. 271 nl_skb = alloc_skb(NLMSG_SPACE(1514), GFP_ATOMIC);  

为发送数据包申请空间。空间数据区大小为1514,即最大ethernet数据包长度。NLMSG_SPACE(1514)返回数据区大小为1514的netlink数据包的大小。详细参考:

http://blog.csdn.net/wangjingfei/archive/2010/02/04/5288263.aspx

[cpp] view plain copy
  1. 282 nlh = nlmsg_put(nl_skb, 0, 0, 0, NLMSG_SPACE(1514) - sizeof(struct nlmsghdr), 0);  

填充netlink数据包头。

[cpp] view plain copy
  1. 283 NETLINK_CB(nl_skb).pid = 0;  

确定发送数据包的进程号,0表示内核进程。该处宏定义同样参考:

http://blog.csdn.net/wangjingfei/archive/2010/02/04/5288263.aspx

[cpp] view plain copy
  1. 290 netlink_unicast(g_nl_sk, nl_skb, pid, MSG_DONTWAIT);  

通过非阻塞方式发送数据包。注意:在发送完数据包之后,nl_skb指向的数据空间将被清空,下一次发送数据包必须重新调用alloc_skb分配空间,否则将会造成内核崩溃,必须重新启动。

 

1.2.4 释放netlink socket

使用完成netlink之后,必须要调用sock_release,否则

[cpp] view plain copy
  1. 45 #define NETLINK_REALNET     26  

指定的协议编号将不再可用。代码如下:

[cpp] view plain copy
  1. 239 void destory_netlink(void)  
  2. 240 {  
  3. 241     if(g_nl_sk != NULL)  
  4. 242     {  
  5. 243         sock_release(g_nl_sk->sk_socket);  
  6. 244     }  
  7. 245 }  
 

 

2. 用户态程序

2.1 相关数据结构

[cpp] view plain copy
  1. 28 // ---------------- For Netlink -------------- //  
  2. 29 struct sockaddr_nl nl_src_addr, nl_dest_addr;  
  3. 30 struct nlmsghdr *nlh = NULL;  
  4. 31 struct iovec iov;  
  5. 32 int nl_fd;  
  6. 33 struct msghdr nl_msg;  
  7. 34 // ------------------------------------------- //  
 

与内核态数据结构类似,不再赘述。

 

2.2 运行过程

2.2.1 初始化netlink

[cpp] view plain copy
  1. 114 void init_nl()  
  2. 115 {  
  3. 116     nl_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_REALNET);  
  4. 117     memset(&nl_msg, 0, sizeof(nl_msg));  
  5. 118     memset(&nl_src_addr, 0, sizeof(nl_src_addr));  
  6. 119     nl_src_addr.nl_family = AF_NETLINK;  
  7. 120     nl_src_addr.nl_pid = getpid();  
  8. 121     nl_src_addr.nl_groups = 0;  
  9. 122       
  10. 123     bind(nl_fd, (struct sockaddr*)&nl_src_addr, sizeof(nl_src_addr));  
  11. 124     memset(&nl_dest_addr, 0, sizeof(nl_dest_addr));  
  12. 125     nl_dest_addr.nl_family = AF_NETLINK;  
  13. 126     nl_dest_addr.nl_pid = 0;   /* For Linux Kernel */  
  14. 127     nl_dest_addr.nl_groups = 0; /* unicast */  
  15. 128       
  16. 129     sendnlmsg("H");  
  17. 130       
  18. 131     memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));  
  19. 132 }    
 
[cpp] view plain copy
  1. 116 nl_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_REALNET);  

创建用户netlink socket,与普通socket创建方法相同,协议族为PF_NETLINK,协议类型为用户自定义的NETLINK_REALNET,与内核态定义相同。

[cpp] view plain copy
  1. 117 memset(&nl_msg, 0, sizeof(nl_msg));  

清空netlink数据包。

[cpp] view plain copy
  1. 118 - 127行  

与普通socket的初始化类似,如果不熟悉可以参考有关socket编程。需要注意的是nl_src_addr的数据结构与普通socket有些不同。

[cpp] view plain copy
  1. 129 sendnlmsg("H");  

向内核进程发送Hello消息,通知内核其进程id。

 

2.2.2 向内核发送消息

以下函数创建并发送netlink消息到内核进程。

[cpp] view plain copy
  1. 36 void sendnlmsg(const char *message)  
  2. 37 {  
  3. 38     printf("Sending: %s/n", message);  
  4. 39     nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));  
  5. 40     nlh ->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);  
  6. 41     nlh -> nlmsg_pid = getpid();  
  7. 42     nlh -> nlmsg_flags = 0;  
  8. 43   
  9. 44     strcpy((char *)NLMSG_DATA(nlh), message);  
  10. 45   
  11. 46     iov.iov_base = (void *)nlh;  
  12. 47     iov.iov_len = nlh->nlmsg_len;  
  13. 48     nl_msg.msg_name = (void *)&nl_dest_addr;  
  14. 49     nl_msg.msg_namelen = sizeof(nl_dest_addr);  
  15. 50     nl_msg.msg_iov = &iov;  
  16. 51     nl_msg.msg_iovlen = 1;  
  17. 52   
  18. 53     printf("Start to send message.");  
  19. 54     sendmsg(nl_fd, &nl_msg, 0);  
  20. 55     printf("Sending finishes./n");  
  21. 56 }  
 
[cpp] view plain copy
  1. 39 nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));  

为netlink header分配存储空间,MAX_PAYLOAD由用户定义,为发送(用户)数据的最大长度。

[cpp] view plain copy
  1. 40 - 51行  

指定netlink相关的参数,准备发送消息。

[cpp] view plain copy
  1. 54 sendmsg(nl_fd, &nl_msg, 0);  

    发送netlink数据包到内核进程,与普通socket中的sendmsg消息用法相同,最后一个参数0表示非阻塞模式。详细参考socket编程。

这篇关于2.6.24以上内核中netlink使用方法的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/810947

相关文章

SpringBoot中使用Flux实现流式返回的方法小结

《SpringBoot中使用Flux实现流式返回的方法小结》文章介绍流式返回(StreamingResponse)在SpringBoot中通过Flux实现,优势包括提升用户体验、降低内存消耗、支持长连... 目录背景流式返回的核心概念与优势1. 提升用户体验2. 降低内存消耗3. 支持长连接与实时通信在Sp

Conda虚拟环境的复制和迁移的四种方法实现

《Conda虚拟环境的复制和迁移的四种方法实现》本文主要介绍了Conda虚拟环境的复制和迁移的四种方法实现,包括requirements.txt,environment.yml,conda-pack,... 目录在本机复制Conda虚拟环境相同操作系统之间复制环境方法一:requirements.txt方法

Nginx 重写与重定向配置方法

《Nginx重写与重定向配置方法》Nginx重写与重定向区别:重写修改路径(客户端无感知),重定向跳转新URL(客户端感知),try_files检查文件/目录存在性,return301直接返回永久重... 目录一.try_files指令二.return指令三.rewrite指令区分重写与重定向重写: 请求

MySQL 打开binlog日志的方法及注意事项

《MySQL打开binlog日志的方法及注意事项》本文给大家介绍MySQL打开binlog日志的方法及注意事项,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要... 目录一、默认状态二、如何检查 binlog 状态三、如何开启 binlog3.1 临时开启(重启后失效)

python使用库爬取m3u8文件的示例

《python使用库爬取m3u8文件的示例》本文主要介绍了python使用库爬取m3u8文件的示例,可以使用requests、m3u8、ffmpeg等库,实现获取、解析、下载视频片段并合并等步骤,具有... 目录一、准备工作二、获取m3u8文件内容三、解析m3u8文件四、下载视频片段五、合并视频片段六、错误

Python中提取文件名扩展名的多种方法实现

《Python中提取文件名扩展名的多种方法实现》在Python编程中,经常会遇到需要从文件名中提取扩展名的场景,Python提供了多种方法来实现这一功能,不同方法适用于不同的场景和需求,包括os.pa... 目录技术背景实现步骤方法一:使用os.path.splitext方法二:使用pathlib模块方法三

Python打印对象所有属性和值的方法小结

《Python打印对象所有属性和值的方法小结》在Python开发过程中,调试代码时经常需要查看对象的当前状态,也就是对象的所有属性和对应的值,然而,Python并没有像PHP的print_r那样直接提... 目录python中打印对象所有属性和值的方法实现步骤1. 使用vars()和pprint()2. 使

CSS实现元素撑满剩余空间的五种方法

《CSS实现元素撑满剩余空间的五种方法》在日常开发中,我们经常需要让某个元素占据容器的剩余空间,本文将介绍5种不同的方法来实现这个需求,并分析各种方法的优缺点,感兴趣的朋友一起看看吧... css实现元素撑满剩余空间的5种方法 在日常开发中,我们经常需要让某个元素占据容器的剩余空间。这是一个常见的布局需求

gitlab安装及邮箱配置和常用使用方式

《gitlab安装及邮箱配置和常用使用方式》:本文主要介绍gitlab安装及邮箱配置和常用使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1.安装GitLab2.配置GitLab邮件服务3.GitLab的账号注册邮箱验证及其分组4.gitlab分支和标签的

SpringBoot3应用中集成和使用Spring Retry的实践记录

《SpringBoot3应用中集成和使用SpringRetry的实践记录》SpringRetry为SpringBoot3提供重试机制,支持注解和编程式两种方式,可配置重试策略与监听器,适用于临时性故... 目录1. 简介2. 环境准备3. 使用方式3.1 注解方式 基础使用自定义重试策略失败恢复机制注意事项