本文转载自: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. [AC自动机模板]Keywords Search

    只是记录一下代码 AC自动机算法的教程请移步这里 还有这里 指针看着懵逼的还可以看一下这里 #include<iostream> #include<cstdio> #inclu ...

  2. 10.5 集合ArrayList 和 io流

    1.ArrayListToFile package day10_io_fileWrite_Read.arraylist_tofile; import java.io.BufferedWriter; i ...

  3. JavaScipt30(第四个案例)(主要知识点:数组原型链上的一些方法)

    承接上文,下面是第四个案例 附上项目链接: https://github.com/wesbos/JavaScript30 const inventors = [ { first: 'Albert', ...

  4. 第四次作业——项目Alpha测试

    这个作业属于哪个课程 <课程链接> 这个作业要求在哪里 <作业要求> 团队名称 飞猪们 这个作业的目标 发布项目α版本,对项目进行用例测试,以及项目情况总结 一.团队成员学号列 ...

  5. git 的 基础操作及使用

    /* git svn版本控制器 */ /*git把文件对应的储存空间分为三个区: 1.工作区 2.缓存区 3.历史区 直接操作文件,不做add时,咱们是在工作区做的修改 右键 git bash her ...

  6. springmvc视图解析

    SpringMVC 视图解析的几种方式: 在视图解析的过程中,需要知道逻辑view的名字,model的名字以访问model和view. 使用jsp进行解析,InternalResourceViewRe ...

  7. manacher(马拉车)算法

    断断续续地看了两天的马拉车算法,可算是给搞明白了(贼开心),这算是自己搞懂的第一个算法了(23333333333333)这个算法照目前自己的理解来看,貌似就只能求个字符串中的回文串(接触这个算法是要求 ...

  8. 洛谷 2484 [SDOI2011]打地鼠

    [题解] n^6的做法很好想,然而这样复杂度不对.. 然后我们可以发现R和C可以分开求,这样复杂度降到了n^4. 使用树状数组可以把复杂度降到n^3logn,可以顺利通过. #include<c ...

  9. [bzoj1500][NOI2005 维修数列] (splay区间操作)

    Description Input 输入的第1 行包含两个数N 和M(M ≤20 000),N 表示初始时数列中数的个数,M表示要进行的操作数目. 第2行包含N个数字,描述初始时的数列. 以下M行,每 ...

  10. Python基础(十一) 异常处理

    在程序运行过程中,总会遇到各种各样的错误,有的错误是程序编写有问题造成的,比如本来应该输出整数结果输出了字符串,这样的错误我们通常称之为BUG,BUG是必须修复的.在Python中内置了一套异常处理机 ...