第十四章 netlink机制--基于Linux3.10【转】
本文转载自:http://blog.csdn.net/shichaog/article/details/44682613
Netlink基于网络的消息机制,能够让用户和内核空间进行通信,12.3节提到的ifconfig是使用ioctl方法和内核通信的,而ip命令则是使用netlink和内核通信的。该机制初衷是为网络服务的,但是现在起应用范围已经大大扩展。
14.1 netlink支持的通信
用户空代码使用实例,发送消息时内核使用同一套代码,也就是说调用这套消息机制代码除了可以发送netlink消息还可以发送其它消息,但是这些消息又各有不同,并且netlink本身也分为好多种,内核在处理这些不同时,使用了两个结构体解决这个问题。
- struct msghdr {
- void * msg_name; /* Socket name */
- int msg_namelen; /* Length of name */
- struct iovec * msg_iov; /* Data blocks */
- __kernel_size_t msg_iovlen; /* Number of blocks */
- void * msg_control; /* Per protocol magic (eg BSD file descriptor passing) */
- __kernel_size_t msg_controllen; /* Length of cmsg list */
- unsigned int msg_flags;
- };
该结构体用于描述不同的消息,msg_iov存放的是消息内容,针对netlink消息有nlmsghdr头来描述。
- struct nlmsghdr {
- __u32 nlmsg_len; /* Length of message including header */
- __u16 nlmsg_type; /* Message content */
- __u16 nlmsg_flags; /* Additional flags */
- __u32 nlmsg_seq; /* Sequence number */
- __u32 nlmsg_pid; /* Sending process port ID */
- };
下面的代码片段展示了netlink的基本用法。
- 12 #define SEND_TEST_DATA "Hello Word"
- 13
- 14 struct event_msg{
- 15 unsigned int event;
- 16 unsigned int sub_event;
- 17 unsigned int len;
- 18 unsigned char data[0];
- 19 };
- 20 /* DEMO SUB EVENT */
- 21 #define LOOP_UNICAST 1
- 22 #define LOOP_BROADCAST 2
- 23
- 24 #define PRIVATE_EVENT_GROUP 2
- 25 #define NETLINK_TEST 17
- 26 #define MAX_PAYLOAD 512 /* maximum payload size*/
- 27 #define TEST_CNT 100000
- 28
- 29 struct hello _info {
- 30 unsigned int idx; //idx
- 31 unsigned int irq_type;
- 32 unsigned long timestamp; //jiffies
- 33 };
- 34
- 35
- 36
- 37 int main(int argc, char* argv[])
- 38 {
- 39 int i;
- 40 struct sockaddr_nl src_addr, dest_addr;
- 41 struct nlmsghdr *nlh = NULL;
- 42 struct iovec iov;
- 43 int sock_fd;
- 44 struct msghdr message,recv_msg;
- 45 struct event_msg *msg;
- 46 struct alarm_info *alarm_info;
- //创建netlink套接字,第三个参数是netlink协议类型,所有的类型见下文。
- 48 sock_fd = socket(PF_NETLINK, SOCK_RAW,NETLINK_TEST);
- 49 memset(&message, 0, sizeof(message));
- 50 memset(&src_addr, 0, sizeof(src_addr));
- 51 src_addr.nl_family = AF_NETLINK;
- 52 src_addr.nl_pid = getpid();
- 53 src_addr.nl_groups = PRIVATE_EVENT_GROUP ;
- /*************************************************************************************
- *******struct sockaddr_nl {
- ******* __kernel_sa_family_t nl_family; /* AF_NETLINK */
- ******* unsigned short nl_pad; /* zero */
- ******* __u32 nl_pid; /* port ID */
- ******* __u32 nl_groups; /* multicast groups mask */
- *******};
- *************************************************************************************/
- 54 bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr)); //将netlink套接字和netlink地址绑定。
- 55 memset(&dest_addr, 0, sizeof(dest_addr));
- 56
- 57 dest_addr.nl_family = AF_NETLINK;
- 58 dest_addr.nl_pid = 0; /* For Linux Kernel */
- 59 dest_addr.nl_groups = PRIVATE_EVENT_GROUP;
- 60
- 61 nlh=(struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));
- 62 /* 参看图14.1;*/
- 63 nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
- 64 nlh->nlmsg_pid = getpid(); /* self pid */
- 65 nlh->nlmsg_flags = 0;
- 66
- 67 for (i = 0;i < TEST_CNT;i++){
- 68 /* Fill in the netlink message payload */
- //将netlink信息和msg关联起来。
- 70 msg = NLMSG_DATA(nlh); //消息头的首部存放netlink的头,见图14.1。
- 71 msg->event = 0;
- 72 msg->sub_event = (i%2) + 1;
- 73 msg->len = sizeof(SEND_TEST_DATA);//Hello Word字符串在payload里了,见图14.1。
- 74 strcpy(msg->data, SEND_TEST_DATA);
- 75
- 76 //printf("test %d time; %s\n",i,(msg->sub_event == LOOP_UNICAST) ? "UNICAST" : "BROADCAST");
- 77 iov.iov_base = (void *)nlh; //这边管理netlink头,也是关联msg,在70行,netlink和msg的关系就确定了。
- 78 iov.iov_len = nlh->nlmsg_len;
- 79 message.msg_name = (void *)&dest_addr;
- 80 message.msg_namelen = sizeof(dest_addr);
- 81 message.msg_iov = &iov;
- 82 message.msg_iovlen = 1;
- 83
- 84 sendmsg(sock_fd, &message, 0); //发送消息给内核
- 85 /* Read message from kernel */
- 86
- 87 memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));
- 88 recvmsg(sock_fd, &message, 0); //从内核接收消息
- 89 msg = NLMSG_DATA(nlh);
- 90 hello _info = (struct hello_info *)&msg->data;
- 91 if ( msg->event == 3)
- 92 {
- 93 printf("recv event %dsub_event %d alarm_info.\n",msg->event, msg->sub_event);
- 96 }
- 97 }
- 98 close(sock_fd);
- 99 return 0;
- 100 }
Netlink消息类型
- Include/uapi/linux/netlink.h
- 8 #define NETLINK_ROUTE 0 /* Routing/device hook */
- 9 #define NETLINK_UNUSED 1 /* Unused number */
- 10 #define NETLINK_USERSOCK 2 /* Reserved for user mode socket protocols */
- 11 #define NETLINK_FIREWALL 3 /* Unused number, formerly ip_queue */
- 12 #define NETLINK_SOCK_DIAG 4 /* socket monitoring */
- 13 #define NETLINK_NFLOG 5 /* netfilter/iptables ULOG */
- 14 #define NETLINK_XFRM 6 /* ip security*/
- 15 #define NETLINK_SELINUX 7 /* SELinux event notifications */
- 16 #define NETLINK_ISCSI 8 /* Open-iSCSI */
- 17 #define NETLINK_AUDIT 9 /* auditing */
- 18 #define NETLINK_FIB_LOOKUP 10
- 19 #define NETLINK_CONNECTOR 11
- 20 #define NETLINK_NETFILTER 12 /* netfilter subsystem */
- 21 #define NETLINK_IP6_FW 13
- 22 #define NETLINK_DNRTMSG 14 /* DECnet routing messages */
- 23 #define NETLINK_KOBJECT_UEVENT 15 /* Kernel messages to userspace */
- 24 #define NETLINK_GENERIC 16
- 25 /* leave room for NETLINK_DM (DM Events) */
- 26 #define NETLINK_SCSITRANSPORT 18 /* SCSI Transports */
- 27 #define NETLINK_ECRYPTFS 19
- 28 #define NETLINK_RDMA 20
- 29 #define NETLINK_CRYPTO 21 /* Crypto layer */
- 30
- 31 #define NETLINK_INET_DIAG NETLINK_SOCK_DIAG
- 32
- 33 #define MAX_LINKS 32
图14.1 netlink消息格式
14.2 netlink用户空间API
在第六章提到套接字创建的系统调用时,提到实际的套接字创建是由具体的协议族的套接字创建函数来完成的,其调用形如err = pf->create(net, sock, protocol, kern);在af_netlink.c文件中netlink协议族的创建netlink套接字的注册的结构体如下。
- static const struct net_proto_family netlink_family_ops = {
- .family = PF_NETLINK,
- .create = netlink_create,
- .owner = THIS_MODULE, /* for consistency 8) */
- };
其创建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。
- 304 static int event_notify_init(void)
- 305 {
- 306 struct sock *nlsock = NULL;
- 307 int ret;
- 308 struct netlink_kernel_cfg cfg = {
- 309 .input = event_notify_receive_skb,
- 310 };
- 311
- 312 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24))
- 313 nlsock = netlink_kernel_create(EVENT_NOTIFY, 0, event_notify_receive_skb, THIS_MODULE);
- 314 #elif (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0))
- 315 nlsock = netlink_kernel_create(&init_net, EVENT_NOTIFY, 0,
- 316 event_notify_receive_skb, NULL, THIS_MODULE);
- 317 #else
- 318 nlsock = netlink_kernel_create(&init_net, EVENT_NOTIFY, &cfg);
- 319 #endif
- 320
- 321 if (nlsock) {
- 322 en_nlsock = nlsock;
- 323 ret = init_events(events_group);
- 324 if (ret) {
- 325 printk(KERN_ERR "some events init fail\n");
- 326 }
- 327 return 0;
- 328 }else{
- 329 printk(KERN_ERR "create netlink %d error\n",EVENT_NOTIFY);
- 330 return -1;
- 331 }
- 332 }
Netlink内核侧的创建函数实际是对__netlink_kernel_create的封装。
- af_netlink.c
- 54 static inline struct sock *
- 55 netlink_kernel_create(struct net *net, int unit, struct netlink_kernel_cfg *cfg)
- 56 {
- 57 return __netlink_kernel_create(net, unit, THIS_MODULE, cfg);
- 58 }
其调用的函数位于af_netlink.c,*net指向所在的网络命名空间,unit是netlink协议类型,module是模块所有者信息,cfg存放的是netlink内核配置参数,配置参数中的input成员用于处理接收到的消息,对于上面的驱动程序只初始化了cfg的input函数指针。该回调函数在应用程序调用sendmsg()发送消息时被调用。
- 2229 struct sock *
- 2230 __netlink_kernel_create(struct net *net, int unit, struct module *module,
- 2231 struct netlink_kernel_cfg *cfg)
- 2232 {
- 2233 struct socket *sock;
- 2234 struct sock *sk;
- 2235 struct netlink_sock *nlk;
- 2236 struct listeners *listeners = NULL;
- 2237 struct mutex *cb_mutex = cfg ? cfg->cb_mutex : NULL;
- 2238 unsigned int groups;
- 2239
- 2240 BUG_ON(!nl_table);
- 2241
- 2242 if (unit < 0 || unit >= MAX_LINKS)
- 2243 return NULL;
- //为sock申请套接字存储空间,并将套接字类型设置为SOCK_DGRAM。
- 2245 if (sock_create_lite(PF_NETLINK, SOCK_DGRAM, unit, &sock))
- 2246 return NULL;
- //按netlink机制需要初始化套接字相应的成员。注意是在初始网络命名空间中完成的。
- 2254 if (__netlink_create(&init_net, sock, cb_mutex, unit) < 0)
- 2255 goto out_sock_release_nosk;
- //更新套接字命名空间
- 2257 sk = sock->sk;
- 2258 sk_change_net(sk, net);
- 2259
- 2260 if (!cfg || cfg->groups < 32)
- 2261 groups = 32;
- 2262 else
- 2263 groups = cfg->groups;
- 2264
- 2265 listeners = kzalloc(sizeof(*listeners) + NLGRPSZ(groups), GFP_KERNEL);
- 2266 if (!listeners)
- 2267 goto out_sock_release;
- 2268
- 2269 sk->sk_data_ready = netlink_data_ready;
- 2270 if (cfg && cfg->input)
- 2271 nlk_sk(sk)->netlink_rcv = cfg->input; //设置netlink消息接收处理函数。
- 2272
- 2273 if (netlink_insert(sk, net, 0))
- 2274 goto out_sock_release;
- 2275
- 2276 nlk = nlk_sk(sk);
- 2277 nlk->flags |= NETLINK_KERNEL_SOCKET;
- 2278
- 2279 netlink_table_grab();//将进程放到nl_table_wait等待链表上,并调度其它进程。
- 2280 if (!nl_table[unit].registered) {
- 2281 nl_table[unit].groups = groups;
- 2282 rcu_assign_pointer(nl_table[unit].listeners, listeners);
- 2283 nl_table[unit].cb_mutex = cb_mutex;
- 2284 nl_table[unit].module = module;
- 2285 if (cfg) {
- 2286 nl_table[unit].bind = cfg->bind;
- 2287 nl_table[unit].flags = cfg->flags;
- 2288 }
- 2289 nl_table[unit].registered = 1;
- 2290 } else {
- 2291 kfree(listeners);
- 2292 nl_table[unit].registered++;
- 2293 }
- 2294 netlink_table_ungrab();
- 2295 return sk;
- 2296
- 2297 out_sock_release:
- 2298 kfree(listeners);
- 2299 netlink_kernel_release(sk);
- 2300 return NULL;
- 2301
- 2302 out_sock_release_nosk:
- 2303 sock_release(sock); //内核关闭netlink套接字API。
- 2304 return NULL;
- 2305 }
Netlink内核发送消息的内核空间API是:
- netlink_unicast
- netlink_broadcast
发送消息的代码片段如下:
- if (pid) {
- /* unicast */
- NETLINK_CB(skb).portid = pid;
- ret = netlink_unicast(nlsock, skb, pid, MSG_DONTWAIT);//单播发送法
- }else{
- /* broadcast */
- NETLINK_CB(skb).dst_group = group;
- ret = netlink_broadcast(nlsock, skb, 0, group, 0);//广播发送法
第十四章 netlink机制--基于Linux3.10【转】的更多相关文章
- “全栈2019”Java多线程第二十四章:等待唤醒机制详解
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- perl 第十四章 Perl5的包和模块
第十四章 Perl5的包和模块 by flamephoenix 一.require函数 1.require函数和子程序库 2.用require指定Perl版本二.包 1.包的定义 2.在包间切 ...
- 第十四章——循环神经网络(Recurrent Neural Networks)(第二部分)
本章共两部分,这是第二部分: 第十四章--循环神经网络(Recurrent Neural Networks)(第一部分) 第十四章--循环神经网络(Recurrent Neural Networks) ...
- Gradle 1.12用户指南翻译——第六十四章. 发布到Ivy(新)
其他章节的翻译请参见:http://blog.csdn.net/column/details/gradle-translation.html翻译项目请关注Github上的地址:https://gith ...
- 【odoo14】第十四章、CMS网站开发
第十四章.CMS网站开发** Odoo有一个功能齐全的内容管理系统(CMS).通过拖放功能,你的最终用户可以在几分钟内设计一个页面,但是在Odoo CMS中开发一个新功能或构建块就不是那么简单了.在本 ...
- Gradle 1.12 翻译——第十四章. 教程 - 杂七杂八
有关其它已翻译的章节请关注Github上的项目:https://github.com/msdx/gradledoc/tree/1.12,或訪问:http://gradledoc.qiniudn.com ...
- JavaScript高级程序设计:第十四章
第十四章 一.表单的基础知识 在HTML中,表单是由<form>元素来表示的,而在javascript中,表单对应的则是HTMLFormElement类型.HTMLFormElement继 ...
- Gradle 1.12用户指南翻译——第二十四章. Groovy 插件
其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Github上的地址: https://g ...
- 第十四章——循环神经网络(Recurrent Neural Networks)(第一部分)
由于本章过长,分为两个部分,这是第一部分. 这几年提到RNN,一般指Recurrent Neural Networks,至于翻译成循环神经网络还是递归神经网络都可以.wiki上面把Recurrent ...
随机推荐
- [AC自动机模板]Keywords Search
只是记录一下代码 AC自动机算法的教程请移步这里 还有这里 指针看着懵逼的还可以看一下这里 #include<iostream> #include<cstdio> #inclu ...
- 10.5 集合ArrayList 和 io流
1.ArrayListToFile package day10_io_fileWrite_Read.arraylist_tofile; import java.io.BufferedWriter; i ...
- JavaScipt30(第四个案例)(主要知识点:数组原型链上的一些方法)
承接上文,下面是第四个案例 附上项目链接: https://github.com/wesbos/JavaScript30 const inventors = [ { first: 'Albert', ...
- 第四次作业——项目Alpha测试
这个作业属于哪个课程 <课程链接> 这个作业要求在哪里 <作业要求> 团队名称 飞猪们 这个作业的目标 发布项目α版本,对项目进行用例测试,以及项目情况总结 一.团队成员学号列 ...
- git 的 基础操作及使用
/* git svn版本控制器 */ /*git把文件对应的储存空间分为三个区: 1.工作区 2.缓存区 3.历史区 直接操作文件,不做add时,咱们是在工作区做的修改 右键 git bash her ...
- springmvc视图解析
SpringMVC 视图解析的几种方式: 在视图解析的过程中,需要知道逻辑view的名字,model的名字以访问model和view. 使用jsp进行解析,InternalResourceViewRe ...
- manacher(马拉车)算法
断断续续地看了两天的马拉车算法,可算是给搞明白了(贼开心),这算是自己搞懂的第一个算法了(23333333333333)这个算法照目前自己的理解来看,貌似就只能求个字符串中的回文串(接触这个算法是要求 ...
- 洛谷 2484 [SDOI2011]打地鼠
[题解] n^6的做法很好想,然而这样复杂度不对.. 然后我们可以发现R和C可以分开求,这样复杂度降到了n^4. 使用树状数组可以把复杂度降到n^3logn,可以顺利通过. #include<c ...
- [bzoj1500][NOI2005 维修数列] (splay区间操作)
Description Input 输入的第1 行包含两个数N 和M(M ≤20 000),N 表示初始时数列中数的个数,M表示要进行的操作数目. 第2行包含N个数字,描述初始时的数列. 以下M行,每 ...
- Python基础(十一) 异常处理
在程序运行过程中,总会遇到各种各样的错误,有的错误是程序编写有问题造成的,比如本来应该输出整数结果输出了字符串,这样的错误我们通常称之为BUG,BUG是必须修复的.在Python中内置了一套异常处理机 ...