本文转载自: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. UIPageViewController 翻页、新手引导--UIScrollView:pagingEnabled

    UIPageViewController 翻页.新手引导--UIScrollView:pagingEnabled

  2. linux shell脚本学习笔记一

    一.文件比较运算符-e filename 如果 filename存在,则为真 [ -e /var/log/syslog ]-d filename 如果 filename为目录,则为真 [ -d /tm ...

  3. java基础学习日志---File方法分析

    package FunDemo; import java.io.File; import java.io.IOException; import java.util.Arrays; public cl ...

  4. <SpringMvc>入门六 异常处理

    如果不做异常处理,那么一直将错误向上抛出,则会最后在页面上显示错误代码 服务启动后,访问test1方法,页面会报500 为了提示友好的错误页面,所以需要做异常处理 1.编写自定义异常类(做提示信息的) ...

  5. Servlet监听器的使用

    Servlet监听器的使用 制作人:全心全意 在Servlet技术中已经定义了一些事件,并且可以针对这些事件来编写相关的事件监听器,从而对事件做出相应的处理.例如,想要在Web应用程序启动和关闭时来执 ...

  6. language support图标消失

    在控制台下输入sudo apt-get install language-selector-gnome即可

  7. Leetcode题目practice

    目录 Leetcode题目解答 1. 删除最外层的括号 2. 两数之和 3. 宝石与石头 4. 移除元素 5.删除排序数组中的重复项 6.寻找两个有序数组的中位数 7.盛最多水的容器 8.存在重复元素 ...

  8. 调用subprocess调用shell命令时屏蔽标准输出

    import os, subprocessp = subprocess.Popen(args, stdout = subprocess.PIPE,stderr = subprocess.STDOUT)

  9. 洛谷 2824 [HEOI2016/TJOI2016]排序

    [题意概述] 对一个1到n的排列做m次区间排序,最后询问位置q上面的数. [题解] 区间排序的效率是nlogn,所以暴力做的话效率是mnlogn,显然达不到要求. 我们考虑二分答案.如果某个位置的数比 ...

  10. [bzoj2242][SDOI2011][计算器] (Baby-Step-Giant-Step+快速幂+exgcd)

    Description 你被要求设计一个计算器完成以下三项任务: 1.给定y,z,p,计算Y^Z Mod P 的值: 2.给定y,z,p,计算满足xy≡ Z ( mod P )的最小非负整数: 3.给 ...