本文转载自:http://blog.csdn.net/shichaog/article/details/44682613

Netlink基于网络的消息机制,能够让用户和内核空间进行通信,12.3节提到的ifconfig是使用ioctl方法和内核通信的,而ip命令则是使用netlink和内核通信的。该机制初衷是为网络服务的,但是现在起应用范围已经大大扩展。

14.1 netlink支持的通信

用户空代码使用实例,发送消息时内核使用同一套代码,也就是说调用这套消息机制代码除了可以发送netlink消息还可以发送其它消息,但是这些消息又各有不同,并且netlink本身也分为好多种,内核在处理这些不同时,使用了两个结构体解决这个问题。

  1. struct msghdr {
  2. void    *   msg_name;   /* Socket name          */
  3. int     msg_namelen;    /* Length of name       */
  4. struct iovec *  msg_iov;    /* Data blocks          */
  5. __kernel_size_t msg_iovlen; /* Number of blocks     */
  6. void    *   msg_control;    /* Per protocol magic (eg BSD file descriptor passing) */
  7. __kernel_size_t msg_controllen; /* Length of cmsg list */
  8. unsigned int    msg_flags;
  9. };

该结构体用于描述不同的消息,msg_iov存放的是消息内容,针对netlink消息有nlmsghdr头来描述。

  1. struct nlmsghdr {
  2. __u32       nlmsg_len;  /* Length of message including header */
  3. __u16       nlmsg_type; /* Message content */
  4. __u16       nlmsg_flags;    /* Additional flags */
  5. __u32       nlmsg_seq;  /* Sequence number */
  6. __u32       nlmsg_pid;  /* Sending process port ID */
  7. };

下面的代码片段展示了netlink的基本用法。

  1. 12 #define SEND_TEST_DATA "Hello Word"
  2. 13
  3. 14 struct event_msg{
  4. 15     unsigned int event;
  5. 16     unsigned int sub_event;
  6. 17     unsigned int len;
  7. 18     unsigned char data[0];
  8. 19 };
  9. 20 /* DEMO SUB EVENT */
  10. 21 #define LOOP_UNICAST                    1
  11. 22 #define LOOP_BROADCAST                  2
  12. 23
  13. 24 #define PRIVATE_EVENT_GROUP   2
  14. 25 #define NETLINK_TEST          17
  15. 26 #define MAX_PAYLOAD           512  /* maximum payload size*/
  16. 27 #define TEST_CNT              100000
  17. 28
  18. 29 struct hello _info {
  19. 30     unsigned int idx;  //idx
  20. 31     unsigned int irq_type;
  21. 32     unsigned long timestamp; //jiffies
  22. 33 };
  23. 34
  24. 35
  25. 36
  26. 37 int main(int argc, char* argv[])
  27. 38 {
  28. 39     int i;
  29. 40     struct sockaddr_nl src_addr, dest_addr;
  30. 41     struct nlmsghdr *nlh = NULL;
  31. 42     struct iovec iov;
  32. 43     int sock_fd;
  33. 44     struct msghdr message,recv_msg;
  34. 45     struct event_msg *msg;
  35. 46     struct alarm_info *alarm_info;
  36. //创建netlink套接字,第三个参数是netlink协议类型,所有的类型见下文。
  37. 48     sock_fd = socket(PF_NETLINK, SOCK_RAW,NETLINK_TEST);
  38. 49     memset(&message, 0, sizeof(message));
  39. 50     memset(&src_addr, 0, sizeof(src_addr));
  40. 51     src_addr.nl_family = AF_NETLINK;
  41. 52     src_addr.nl_pid = getpid();
  42. 53     src_addr.nl_groups = PRIVATE_EVENT_GROUP ;
  43. /*************************************************************************************
  44. *******struct sockaddr_nl {
  45. ******* __kernel_sa_family_t    nl_family;  /* AF_NETLINK   */
  46. ******* unsigned short  nl_pad;     /* zero     */
  47. ******* __u32       nl_pid;     /* port ID  */
  48. *******         __u32       nl_groups;  /* multicast groups mask */
  49. *******};
  50. *************************************************************************************/
  51. 54     bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr));  //将netlink套接字和netlink地址绑定。
  52. 55     memset(&dest_addr, 0, sizeof(dest_addr));
  53. 56
  54. 57     dest_addr.nl_family = AF_NETLINK;
  55. 58     dest_addr.nl_pid = 0;   /* For Linux Kernel */
  56. 59     dest_addr.nl_groups = PRIVATE_EVENT_GROUP;
  57. 60
  58. 61     nlh=(struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));
  59. 62     /* 参看图14.1;*/
  60. 63     nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
  61. 64     nlh->nlmsg_pid = getpid();  /* self pid */
  62. 65     nlh->nlmsg_flags = 0;
  63. 66
  64. 67     for (i = 0;i < TEST_CNT;i++){
  65. 68         /* Fill in the netlink message payload */
  66. //将netlink信息和msg关联起来。
  67. 70         msg = NLMSG_DATA(nlh); //消息头的首部存放netlink的头,见图14.1。
  68. 71         msg->event = 0;
  69. 72         msg->sub_event = (i%2) + 1;
  70. 73         msg->len = sizeof(SEND_TEST_DATA);//Hello Word字符串在payload里了,见图14.1。
  71. 74         strcpy(msg->data, SEND_TEST_DATA);
  72. 75
  73. 76         //printf("test %d time; %s\n",i,(msg->sub_event == LOOP_UNICAST) ? "UNICAST" : "BROADCAST");
  74. 77         iov.iov_base = (void *)nlh; //这边管理netlink头,也是关联msg,在70行,netlink和msg的关系就确定了。
  75. 78         iov.iov_len = nlh->nlmsg_len;
  76. 79         message.msg_name = (void *)&dest_addr;
  77. 80         message.msg_namelen = sizeof(dest_addr);
  78. 81         message.msg_iov = &iov;
  79. 82         message.msg_iovlen = 1;
  80. 83
  81. 84         sendmsg(sock_fd, &message, 0); //发送消息给内核
  82. 85         /* Read message from kernel */
  83. 86
  84. 87         memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));
  85. 88         recvmsg(sock_fd, &message, 0); //从内核接收消息
  86. 89         msg = NLMSG_DATA(nlh);
  87. 90         hello _info = (struct hello_info *)&msg->data;
  88. 91         if ( msg->event == 3)
  89. 92         {
  90. 93             printf("recv event %dsub_event %d  alarm_info.\n",msg->event, msg->sub_event);
  91. 96         }
  92. 97     }
  93. 98     close(sock_fd);
  94. 99     return 0;
  95. 100 }

Netlink消息类型

  1. Include/uapi/linux/netlink.h
  2. 8 #define NETLINK_ROUTE       0   /* Routing/device hook              */
  3. 9 #define NETLINK_UNUSED      1   /* Unused number                */
  4. 10 #define NETLINK_USERSOCK    2   /* Reserved for user mode socket protocols  */
  5. 11 #define NETLINK_FIREWALL    3   /* Unused number, formerly ip_queue     */
  6. 12 #define NETLINK_SOCK_DIAG   4   /* socket monitoring                */
  7. 13 #define NETLINK_NFLOG       5   /* netfilter/iptables ULOG */
  8. 14 #define NETLINK_XFRM        6   /* ip security*/
  9. 15 #define NETLINK_SELINUX     7   /* SELinux event notifications */
  10. 16 #define NETLINK_ISCSI       8   /* Open-iSCSI */
  11. 17 #define NETLINK_AUDIT       9   /* auditing */
  12. 18 #define NETLINK_FIB_LOOKUP  10
  13. 19 #define NETLINK_CONNECTOR   11
  14. 20 #define NETLINK_NETFILTER   12  /* netfilter subsystem */
  15. 21 #define NETLINK_IP6_FW      13
  16. 22 #define NETLINK_DNRTMSG     14  /* DECnet routing messages */
  17. 23 #define NETLINK_KOBJECT_UEVENT  15  /* Kernel messages to userspace */
  18. 24 #define NETLINK_GENERIC     16
  19. 25 /* leave room for NETLINK_DM (DM Events) */
  20. 26 #define NETLINK_SCSITRANSPORT   18  /* SCSI Transports */
  21. 27 #define NETLINK_ECRYPTFS    19
  22. 28 #define NETLINK_RDMA        20
  23. 29 #define NETLINK_CRYPTO      21  /* Crypto layer */
  24. 30
  25. 31 #define NETLINK_INET_DIAG   NETLINK_SOCK_DIAG
  26. 32
  27. 33 #define MAX_LINKS 32

图14.1 netlink消息格式

14.2 netlink用户空间API

在第六章提到套接字创建的系统调用时,提到实际的套接字创建是由具体的协议族的套接字创建函数来完成的,其调用形如err = pf->create(net, sock, protocol, kern);在af_netlink.c文件中netlink协议族的创建netlink套接字的注册的结构体如下。

  1. static const struct net_proto_family netlink_family_ops = {
  2. .family = PF_NETLINK,
  3. .create = netlink_create,
  4. .owner  = THIS_MODULE,  /* for consistency 8) */
  5. };

其创建netlink套接字的过程和inet套接字是一样的。和inet协议很相似,netlink实现相关的主要代码在af_netlink.c(inet也有一个af_inet.c)文件。

14.3 netlink内核空间API

14.2节的内容是针对用户空间的,这本小节则是针对内核而言的。Netlink内核创建API位于include/Linux/netlink.h。在14.1节的netlink应用程序调用14.2节的netlink套接字创建API创建netlink套接字,并发送了一个netlink消息,在内核侧有对应的netlink套接字接收应用程序发送的消息。内核侧创建netlink消息方法和应用程序调用的接口并不一样。接收应用程序发送的消息的内核侧驱动程序netlink创建的netlink代码片段可以看出。312~319行可以看到netlink套接字创建接口的参数随着内核版本的升级发生了一些变化。本文基于3.10内核,所以创建的API是318行代码中的netlink_kernel_create。

  1. 304 static int event_notify_init(void)
  2. 305 {
  3. 306     struct sock *nlsock = NULL;
  4. 307     int ret;
  5. 308     struct netlink_kernel_cfg cfg = {
  6. 309         .input  = event_notify_receive_skb,
  7. 310     };
  8. 311
  9. 312 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24))
  10. 313     nlsock = netlink_kernel_create(EVENT_NOTIFY, 0, event_notify_receive_skb, THIS_MODULE);
  11. 314 #elif (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0))
  12. 315     nlsock = netlink_kernel_create(&init_net, EVENT_NOTIFY, 0,
  13. 316             event_notify_receive_skb, NULL, THIS_MODULE);
  14. 317 #else
  15. 318     nlsock = netlink_kernel_create(&init_net, EVENT_NOTIFY, &cfg);
  16. 319 #endif
  17. 320
  18. 321     if (nlsock) {
  19. 322         en_nlsock = nlsock;
  20. 323         ret = init_events(events_group);
  21. 324         if (ret) {
  22. 325             printk(KERN_ERR "some events init fail\n");
  23. 326         }
  24. 327         return 0;
  25. 328     }else{
  26. 329         printk(KERN_ERR "create netlink %d error\n",EVENT_NOTIFY);
  27. 330         return -1;
  28. 331     }
  29. 332 }

Netlink内核侧的创建函数实际是对__netlink_kernel_create的封装。

  1. af_netlink.c
  2. 54 static inline struct sock *
  3. 55 netlink_kernel_create(struct net *net, int unit, struct netlink_kernel_cfg *cfg)
  4. 56 {
  5. 57     return __netlink_kernel_create(net, unit, THIS_MODULE, cfg);
  6. 58 }

其调用的函数位于af_netlink.c,*net指向所在的网络命名空间,unit是netlink协议类型,module是模块所有者信息,cfg存放的是netlink内核配置参数,配置参数中的input成员用于处理接收到的消息,对于上面的驱动程序只初始化了cfg的input函数指针。该回调函数在应用程序调用sendmsg()发送消息时被调用。

  1. 2229 struct sock *
  2. 2230 __netlink_kernel_create(struct net *net, int unit, struct module *module,
  3. 2231             struct netlink_kernel_cfg *cfg)
  4. 2232 {
  5. 2233     struct socket *sock;
  6. 2234     struct sock *sk;
  7. 2235     struct netlink_sock *nlk;
  8. 2236     struct listeners *listeners = NULL;
  9. 2237     struct mutex *cb_mutex = cfg ? cfg->cb_mutex : NULL;
  10. 2238     unsigned int groups;
  11. 2239
  12. 2240     BUG_ON(!nl_table);
  13. 2241
  14. 2242     if (unit < 0 || unit >= MAX_LINKS)
  15. 2243         return NULL;
  16. //为sock申请套接字存储空间,并将套接字类型设置为SOCK_DGRAM。
  17. 2245     if (sock_create_lite(PF_NETLINK, SOCK_DGRAM, unit, &sock))
  18. 2246         return NULL;
  19. //按netlink机制需要初始化套接字相应的成员。注意是在初始网络命名空间中完成的。
  20. 2254     if (__netlink_create(&init_net, sock, cb_mutex, unit) < 0)
  21. 2255         goto out_sock_release_nosk;
  22. //更新套接字命名空间
  23. 2257     sk = sock->sk;
  24. 2258     sk_change_net(sk, net);
  25. 2259
  26. 2260     if (!cfg || cfg->groups < 32)
  27. 2261         groups = 32;
  28. 2262     else
  29. 2263         groups = cfg->groups;
  30. 2264
  31. 2265     listeners = kzalloc(sizeof(*listeners) + NLGRPSZ(groups), GFP_KERNEL);
  32. 2266     if (!listeners)
  33. 2267         goto out_sock_release;
  34. 2268
  35. 2269     sk->sk_data_ready = netlink_data_ready;
  36. 2270     if (cfg && cfg->input)
  37. 2271         nlk_sk(sk)->netlink_rcv = cfg->input; //设置netlink消息接收处理函数。
  38. 2272
  39. 2273     if (netlink_insert(sk, net, 0))
  40. 2274         goto out_sock_release;
  41. 2275
  42. 2276     nlk = nlk_sk(sk);
  43. 2277     nlk->flags |= NETLINK_KERNEL_SOCKET;
  44. 2278
  45. 2279     netlink_table_grab();//将进程放到nl_table_wait等待链表上,并调度其它进程。
  46. 2280     if (!nl_table[unit].registered) {
  47. 2281         nl_table[unit].groups = groups;
  48. 2282         rcu_assign_pointer(nl_table[unit].listeners, listeners);
  49. 2283         nl_table[unit].cb_mutex = cb_mutex;
  50. 2284         nl_table[unit].module = module;
  51. 2285         if (cfg) {
  52. 2286             nl_table[unit].bind = cfg->bind;
  53. 2287             nl_table[unit].flags = cfg->flags;
  54. 2288         }
  55. 2289         nl_table[unit].registered = 1;
  56. 2290     } else {
  57. 2291         kfree(listeners);
  58. 2292         nl_table[unit].registered++;
  59. 2293     }
  60. 2294     netlink_table_ungrab();
  61. 2295     return sk;
  62. 2296
  63. 2297 out_sock_release:
  64. 2298     kfree(listeners);
  65. 2299     netlink_kernel_release(sk);
  66. 2300     return NULL;
  67. 2301
  68. 2302 out_sock_release_nosk:
  69. 2303     sock_release(sock); //内核关闭netlink套接字API。
  70. 2304     return NULL;
  71. 2305 }

Netlink内核发送消息的内核空间API是:

  1. netlink_unicast
  2. netlink_broadcast

发送消息的代码片段如下:

  1. if (pid) {
  2. /* unicast */
  3. NETLINK_CB(skb).portid = pid;
  4. ret = netlink_unicast(nlsock, skb, pid, MSG_DONTWAIT);//单播发送法
  5. }else{
  6. /* broadcast */
  7. NETLINK_CB(skb).dst_group = group;
  8. ret = netlink_broadcast(nlsock, skb, 0, group, 0);//广播发送法

第十四章 netlink机制--基于Linux3.10【转】的更多相关文章

  1. “全栈2019”Java多线程第二十四章:等待唤醒机制详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  2. perl 第十四章 Perl5的包和模块

    第十四章 Perl5的包和模块 by flamephoenix 一.require函数  1.require函数和子程序库  2.用require指定Perl版本二.包  1.包的定义  2.在包间切 ...

  3. 第十四章——循环神经网络(Recurrent Neural Networks)(第二部分)

    本章共两部分,这是第二部分: 第十四章--循环神经网络(Recurrent Neural Networks)(第一部分) 第十四章--循环神经网络(Recurrent Neural Networks) ...

  4. Gradle 1.12用户指南翻译——第六十四章. 发布到Ivy(新)

    其他章节的翻译请参见:http://blog.csdn.net/column/details/gradle-translation.html翻译项目请关注Github上的地址:https://gith ...

  5. 【odoo14】第十四章、CMS网站开发

    第十四章.CMS网站开发** Odoo有一个功能齐全的内容管理系统(CMS).通过拖放功能,你的最终用户可以在几分钟内设计一个页面,但是在Odoo CMS中开发一个新功能或构建块就不是那么简单了.在本 ...

  6. Gradle 1.12 翻译——第十四章. 教程 - 杂七杂八

    有关其它已翻译的章节请关注Github上的项目:https://github.com/msdx/gradledoc/tree/1.12,或訪问:http://gradledoc.qiniudn.com ...

  7. JavaScript高级程序设计:第十四章

    第十四章 一.表单的基础知识 在HTML中,表单是由<form>元素来表示的,而在javascript中,表单对应的则是HTMLFormElement类型.HTMLFormElement继 ...

  8. Gradle 1.12用户指南翻译——第二十四章. Groovy 插件

    其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Github上的地址: https://g ...

  9. 第十四章——循环神经网络(Recurrent Neural Networks)(第一部分)

    由于本章过长,分为两个部分,这是第一部分. 这几年提到RNN,一般指Recurrent Neural Networks,至于翻译成循环神经网络还是递归神经网络都可以.wiki上面把Recurrent ...

随机推荐

  1. HDU_1584_(DFS)

    蜘蛛牌 Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submis ...

  2. node-sass安装失败的解决方案

    这是一个老生常谈的问题了,网上有很多解决方法,找一个自己觉得合适的才是最重要的...... 执行以下命令即可: npm config set sass_binary_site https://npm. ...

  3. [Git]Please make sure you have the correct access rights and the repository exists

    这个问题是这样,需要在已有github账号的A机器上,再创建一个github账号,新账号创建完毕,将代码通过机器A push上之后,再另一台机器B,clone 这个项目时报出了如下错误: Permis ...

  4. 洛谷——P3807 【模板】卢卡斯定理

    P3807 [模板]卢卡斯定理 洛谷智推模板题,qwq,还是太弱啦,组合数基础模板题还没做过... 给定n,m,p($1\le n,m,p\le 10^5$) 求 $C_{n+m}^{m}\ mod\ ...

  5. MySQL练习题及答案(复习)

    新建一个叫做 review 的数据库,将测试数据脚本导进去.(可以使用Navicat查询功能) /* Navicat MySQL Data Transfer Source Server : DB So ...

  6. Error:Cannot find bean: "org.apache.struts.taglib.html.BEAN" in any scope

    原因:html标签和struts标签混用,或者表单元素外面没有<html:form>标签包裹. 解决:统一标签 <html:form action="/login.do&q ...

  7. python3返回值中的none

    浏览器返回null,python3返回none,懵了. google了很多资料,不明就里,这就是没基础的后果啊呀呀呀. 上阮一峰的截图,就这么理解下凑合吧:

  8. FZU 1492 地震预测(模拟链表的应用)(Java实现)

    FZU 1492 地震预测(模拟链表的应用)(Java实现) 怀特先生是一名研究地震的科学家,最近他发现如果知道某一段时间内的地壳震动能量采样的最小波动值之和,可以有效地预测大地震的发生. 假设已知一 ...

  9. xe的debug怪现象

    死活有问题,而且不能重新编译生成文件. 查网上说明:在删除项目xxx.dproj文件后,然后打开dpk文件,会自动生成.dproj文件,再然后一切OK. 的确如此,但莫名其妙.

  10. Spring MVC学习总结(11)——Spring MVC集成Swagger跨域问题

      <!-- CORS配置,为了让别的机器访问本机的swagger接口文档服务 -->          <dependency>              <group ...