本文转载自: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. 如何在linux使用nmap端口扫描工具扫描网段内开放的端口

    在另一个linux主机上,使用nmap命令即可 ,比如 我在1.1.1.2上开放了端口1111 -A -j ACCEPT 在1.1.1.1上执行 即可查到

  2. python实现二叉树的遍历以及基本操作

    主要内容: 二叉树遍历(先序.中序.后序.宽度优先遍历)的迭代实现和递归实现: 二叉树的深度,二叉树到叶子节点的所有路径: 首先,先定义二叉树类(python3),代码如下: class TreeNo ...

  3. ThinkPHP---thinkphp文件加载

    [一]文件加载在ThinkPHP里提供了三种方式 实际开发里,文件加载方式一般以第一种为主(通过函数库形式自动加载,此时我们仅仅需要定义文件和函数) (1)函数库形式加载 函数库分3种级别,系统函数库 ...

  4. 16 this和super和构造代码块

    this关键词---当前类的对象的引用 public class Public { String name; int age; public static void main(String[] arg ...

  5. 如何做到在webpack打包vue项目后,在外部动态修改配置文件

    在我们做完vue项目后,只需要执行 npm run dist 就可以轻松进行打包转测试,可是如果我们临时需要修改一些配置文件比如域名,这时候我们就有点懵逼了,那就修改了再重新打一次包? NO NO N ...

  6. [Luogu] P1407 [国家集训队]稳定婚姻

    题目描述 我国的离婚率连续7年上升,今年的头两季,平均每天有近5000对夫妇离婚,大城市的离婚率上升最快,有研究婚姻问题的专家认为,是与简化离婚手续有关. 25岁的姗姗和男友谈恋爱半年就结婚,结婚不到 ...

  7. TestNG忽略测试

    用@Test(enabled = false) 声明需要被忽略执行的测试方法 package com.janson; import org.testng.annotations.Test; publi ...

  8. HDU - 6446 Tree and Permutation

    传送门:http://acm.hdu.edu.cn/showproblem.php?pid=6446 本题是一个树上的问题——DFS. 一棵N个结点的树,其结点为1~N.树具有N-1条边,每一条边具有 ...

  9. codeforces gym 100357 I (费用流)

    题目大意 给出一个或与表达式,每个正变量和反变量最多出现一次,询问是否存在一种方案使得每个或式中有且仅有一个变量的值为1. 解题分析 将每个变量拆成三个点x,y,z. y表示对应的正变量,z表示对应的 ...

  10. Memory+SLES 11/12 OS Tuning & Optimization

    https://www.suse.com/documentation/sles11/book_sle_tuning/data/sec_util_memory.html SLES 11/12 OS Tu ...