测试环境: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 相关的数据结构变量:

  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 // ----------------------------------------- //
  1. 45 #define NETLINK_REALNET     26

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

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

  1. 46 struct sock *g_nl_sk = NULL;

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

  1. 48 struct sockaddr_nl src_addr, dest_addr;

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

  1. 50 struct iovec iov;

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

  1. 53 struct msghdr msg;

netlink消息头。

1.2 调用过程

1.2.1 创建netlink socket

  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,则清除与该进程的联系。

  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 }
  1. 191 skb = skb_get (__skb);

获取实际数据包。

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

  1. 195 nlh = nlmsg_hdr(skb);

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

  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

  1. 213 kfree_skb(skb);

释放接收到的消息。

1.2.3 向用户进程发送netlink消息

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

  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 }
  1. 247 void send_to_user(struct sk_buff *skb)

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

  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

  1. 282 nlh = nlmsg_put(nl_skb, 0, 0, 0, NLMSG_SPACE(1514) - sizeof(struct nlmsghdr), 0);

填充netlink数据包头。

  1. 283 NETLINK_CB(nl_skb).pid = 0;

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

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

  1. 290 netlink_unicast(g_nl_sk, nl_skb, pid, MSG_DONTWAIT);

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

1.2.4 释放netlink socket

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

  1. 45 #define NETLINK_REALNET     26

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

  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 相关数据结构

  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

  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 }
  1. 116 nl_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_REALNET);

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

  1. 117 memset(&nl_msg, 0, sizeof(nl_msg));

清空netlink数据包。

  1. 118 - 127行

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

  1. 129 sendnlmsg("H");

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

2.2.2 向内核发送消息

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

  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 }
  1. 39 nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));

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

  1. 40 - 51行

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

  1. 54 sendmsg(nl_fd, &nl_msg, 0);

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

netlink使用方法相关推荐

  1. linux 内核与用户空间通信之netlink使用方法

    Linux中的进程间通信机制源自于Unix平台上的进程通信机制.Unix的两大分支AT&T Unix和BSD Unix在进程通信实现机制上的各有所不同,前者形成了运行在单个计算机上的Syste ...

  2. NetLink机制使用

    前些日子研究如何在Android实现USB-Audio的热插拔,顺带了解了一下netlink机制.netlink在TCP/IP方面用得较多,但根据需要也可用在HDMI/USB等热插拔消息通知.前人已经 ...

  3. linux内核之netlink通信

    Linux内核(04)之netlink通信 Author:Onceday Date:2023年1月3日 漫漫长路,才刚刚开始- 参考文档: netlink 机制 binarydady 阿里云开发者社区 ...

  4. linux内核打开文件数,放开Linux内核对用户进程可打开文件数和TCP连接的限制

    一. 检查linux内核 uname -a lsb_release -a 二.用户进程可打开文件数限制 1)vim /etc/security/limits.conf *       -      n ...

  5. linux 内存管理 (一) 基本的一些概念

    该文章参考宋宝华老师的内存管理课程,详细可以去听阅码场宋老师的课程. 本文将从 从硬件原理  到内核实现   配置内存的参数(内存什么时候回收,脏数据什么时候收回?)  从应用程序上的内存泄漏 还有工 ...

  6. 在 Oracle Enterprise Linux 和 iSCSI 上构建您自己的 Oracle RAC 11g 集群

    作者:Jeffrey Hunter 了解如何以低于 2,700 美元的费用在 Oracle Enterprise Linux 上安装并配置 Oracle RAC 11g 第 2 版开发集群. 本指南中 ...

  7. Java面试题大全2021版

    一.Java 基础 JDK 和 JRE 有什么区别? JDK:Java Development Kit 的简称,java 开发工具包,提供了 java 的开发环境和运行环境. JRE:Java Run ...

  8. Netlink的简介及使用方法

    1.Netlink socket的作用: Netlink socket 是一种Linux特有的socket,用于实现用户进程与内核进程之间通信的一种特殊的进程间通信方式(IPC) ,也是网络应用程序与 ...

  9. netlink实现与使用方法详解(用户态/内核态)

    一.什么是netlink Netlink套接字是用以实现用户进程与内核进程通信的一种特殊的进程间通信(IPC) ,也是网络应用程序与内核通信的最常用的接口. 在Linux 内核中,使用netlink ...

最新文章

  1. pugixml库的一个使用心得
  2. Spark源码的编译过程详细解读(各版本)(博主推荐)
  3. Spring Cloud Feign 负载均衡
  4. poj2586 Y2K Accounting Bug
  5. 面试官问:断网了,还能ping通 127.0.0.1 吗?为什么?
  6. matplotlib 显示批量图片_matplotlib入门
  7. 计算机网络(五)——组建客户机/服务器网络
  8. Selenium API-WebDriver 属性
  9. 对长度为200的有序表进行二分查找_程序员常用的查找算法(顺序、二分、插值、分块、斐波那契)...
  10. linux服务器学习笔记:linux如何远程登录?
  11. Codeforces 832 D Misha, Grisha and Underground
  12. 二维光子晶体带隙仿真Matlab完全程序_平面波展开法
  13. mysql 左连接与右连接的区别吗_数据库左连接和右连接有什么区别
  14. 打印机扫描显示服务器没有响应,打印机扫描一体机能够打印却不能扫描,提示缺少WIA的驱动程序...
  15. border边框属性的介绍
  16. 计算机软件开发属于什么类的专业,软件工程专业属于哪个类别?
  17. HyperAI超神经已加入群聊,思否 AIGC Hackathon 扩列
  18. 【Android】Android Window
  19. Springboot课程教学平台设计与实现4mu05计算机毕业设计-课程设计-期末作业-毕设程序代做
  20. 惯性导航的定位原理是什么?

热门文章

  1. oracle11 如何启动企业管理器
  2. vivo X Note暗藏黑科技,三麦降噪让开黑更安心
  3. 《文明六:Sid Meier's Civilization VI 》的介绍
  4. 解决pip装包报错ERROR: Cannot unistall ‘llvmlite‘. It is a distutils installed project and thus we cannot..
  5. 喜大普奔,又一国标正式进入国际标准
  6. 程序员幽默:当程序员当了爸爸
  7. 解决mysql sum求和返回null问题或IFNULL应用
  8. 基于思维导图的研究生创新能力培养
  9. 骁龙8+参数 骁龙8+什么水平 骁龙8+处理器怎么样
  10. 【数据挖掘】聚类分析实例