本文转载自: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. 创建一个TCP服务器端通信程序的步骤

    创建一个TCP服务器端通信程序的步骤: 1). 创建一个ServerSocket 2). 从ServerSocket接受客户连接请求 3). 创建一个服务线程处理新的连接 4). 在服务线程中,从so ...

  2. 梦想3D控件 2018.7.26更新

    下载地址: http://www.mxdraw.com/ndetail_108.html 1.  编写所有接口函数使用的CHM文档 2.  增加交互绘制功能 3.  增加案例弧形窗建模案例 4.  增 ...

  3. Mybatis与Spring整合方法

    实现mybatis与spring进行整合,通过spring管理SqlSessionFactory.mapper接口. tips:mybatis官方提供与mybatis与spring整合jar包. 一. ...

  4. 12Java Bean

     Java Bean JavaBean是一种组件体系结构.实际上,JavaBean就是一个Java类,这个类可以重复地使用.我们可以把JavaBean看成是一个黑盒子,即只需要知道其功能而不必管其内部 ...

  5. 使用Sophus练习李群SO3、SE3以及对应的李代数so3、se3

    这是高博<视觉SLAM14讲,从理论到实践>第4章的练习.加了一些注释和理解: #include <iostream>#include <cmath>using n ...

  6. 解决docker容器启动时候无法映射端口的问题

    当我们停止防火墙后,docker容器启动映射端口可能无法映射端口,这个时候需要重建docker0网桥. 详细的错误是这样的: docker: Error response from daemon: d ...

  7. JAVA基础——链表结构之双端链表

    双端链表:双端链表与传统链表非常相似.只是新增了一个属性-即对最后一个链结点的引用 如上图所示:由于有着对最后一个链结点的直接引用.所以双端链表比传统链表在某些方面要方便.比如在尾部插入一个链结点.双 ...

  8. TWaver矢量小试——Android演进路线图

    还有半个多月就到春节了,年底相信很多公司都会进行年度总结以及公司发展状况总结,在这过程中难免会用到RoadMap,在这我们也使用TWaver的矢量部分绘制一个Android系统的发展历程.先看效果:什 ...

  9. 关押罪犯 - 并查集&优先队列

    题目地址:http://www.51cpc.com/web/problem.php?id=4261 Summarize: 此题最巧妙的是“敌人的敌人就是朋友!”,故需先将敌对关系放入优先队列,按怨恨值 ...

  10. Python学习第二阶段day1 内置函数,序列化,软件目录开发规范

    内置函数 1.abs()  求绝对值 2.all()    所有元素为真才返回真 all( [1,1,2,3,-1] ) 值为True 3.any()   所有元素为假才返回假  any([0,0,0 ...