netlink快速使用例程(linux-3.3.8)
2017-05-18 20:23
204 查看
netlink快速使用例程(linux-3.3.8)
由于我调试的系统是基于LSDK的,开发按键功能时,移植了hotplug,但是内核上报了相关的事件,但是应用层没有监控。于是,自己参照写了一个netlink的应用层接口。(openwrt是用procd实现的,已经完成了一整套的接口)Linux-3.10.36版本的内核相比linux-2.6.36版本中netlink的代码有所变化,以前的代码已经不能成功编译了。
netlink是linux内核的一套基于socket的通信机制,那么,只需要知道怎么创建套接字,发送数据,接收数据就行了。
内核层:
netlink API
初始化操作:
新的netlink_kernel_create()函数只有3个参数了。/****************************** net: linux网络命名空间结构体指针 uint: netlink协议类型 cfg: netlink内核配置参数结构体指针 返回: sock结构指针 ******************************/ static inline struct sock * netlink_kernel_create(struct net *net, int unit, struct netlink_kernel_cfg *cfg)
[/code]
第一个参数:传入一个网络命名空间结构的地址,这块我不怎么清楚,只知道linux的网络命名空间的出现是为了让 用户创建的进程能够与系统分离得更加彻底,从而不需要使用更多的底层虚拟化技术。未深究,调用该函数时就传入&init_net吧,它是在net_namespace.c文件中定义的一个结构体。
第二个参数:是代表netlink协议类型,内核目前定义了如下一些类型:
#define NETLINK_ROUTE 0 /* Routing/device hook */ #define NETLINK_UNUSED 1 /* Unused number */ #define NETLINK_USERSOCK 2 /* Reserved for user mode socket protocols */ #define NETLINK_FIREWALL 3 /* Unused number, formerly ip_queue */ #define NETLINK_SOCK_DIAG 4 /* socket monitoring */ #define NETLINK_NFLOG 5 /* netfilter/iptables ULOG */ #define NETLINK_XFRM 6 /* ipsec */ #define NETLINK_SELINUX 7 /* SELinux event notifications */ #define NETLINK_ISCSI 8 /* Open-iSCSI */ #define NETLINK_AUDIT 9 /* auditing */ #define NETLINK_FIB_LOOKUP 10 #define NETLINK_CONNECTOR 11 #define NETLINK_NETFILTER 12 /* netfilter subsystem */ #define NETLINK_IP6_FW 13 #define NETLINK_DNRTMSG 14 /* DECnet routing messages */ #define NETLINK_KOBJECT_UEVENT 15 /* Kernel messages to userspace */ #define NETLINK_GENERIC 16 /* leave room for NETLINK_DM (DM Events) */ #define NETLINK_SCSITRANSPORT 18 /* SCSI Transports */ #define NETLINK_ECRYPTFS 19 #define NETLINK_RDMA 20 #define NETLINK_CRYPTO 21 /* Crypto layer */ #define NETLINK_INET_DIAG NETLINK_SOCK_DIAG #define MAX_LINKS 32
[/code]
最多可以定义32中类型,若 uint > MAX_LINKS 则该函数返回NULL,源代码片段如下
__netlink_kernel_create(struct net *net, int unit, struct module *module, struct netlink_kernel_cfg *cfg) { struct socket *sock; struct sock *sk; struct netlink_sock *nlk; struct listeners *listeners = NULL; struct mutex *cb_mutex = cfg ? cfg->cb_mutex : NULL; unsigned int groups; BUG_ON(!nl_table); if (unit < 0 || unit >= MAX_LINKS) return NULL; ... }
[/code]
第三个参数:cfg存放的是netlink内核配置参数,配置参数中的input成员用于处理接收到的消息。该函数的参数变短了,实际是因为放到该结构体中来了
struct netlink_kernel_cfg { unsigned int groups;// netlink组,应该是多播时使用,单播的情况就是0了 unsigned int flags; void (*input)(struct sk_buff *skb); //回调函数,当收到消息时,内核会调用该函数指针指向的函数进行处理 struct mutex *cb_mutex; void (*bind)(int group); };
[/code]
发送
单播netlink_unicast() 和 多播netlink_broadcast()/****************************** ssk: sock结构体指针 skb: skb存放消息,它的data字段指向要发送的 netlink消息结构,而skb的控制块保存了消息的地址信息,前面的宏NETLINK_CB(skb)就用于方便设置该控制块 portid: 端口id nonblock:表示该函数是否为非阻塞,如果为1,该函数将在没有接收缓存可利用时立即返回,而如果为0,该函数在没有接收缓存可利 用时睡眠 返回: 发送数据的长度 ******************************/ int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 portid, int nonblock);
[/code]
/****************************** ssk: sock结构体指针 skb: skb存放消息,它的data字段指向要发送的 netlink消息结构,而skb的控制块保存了消息的地址信息,前面的宏NETLINK_CB(skb)就用于方便设置该控制块 portid: 端口id group: netlink组 allocation: 内核内存分配类型,一般地为GFP_ATOMIC或 GFP_KERNEL,GFP_ATOMIC用于原子的上下文(即不可以睡眠),而GFP_KERNEL用于非原子上下文 返回: 发送数据的长度 ******************************/ int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, __u32 portid, __u32 group, gfp_t allocation);
[/code]
接收
在调用netlink_kernel_create()函数是已经向cfg结构体中指定了回调函数,这个函数就是接收函数了。内核模块收到消息后会自动调用该函数。用户层
使用标准的socket API即可( socket(), bind(), sendmsg(), recvmsg() 和 close())消息结构
如图:源代码
示例代码由用户层向内核发送一个字符串,内核打印接收到的消息并且将字符串发送给用户层。内核层:
/******************************* file: k_netlink.c description: netlink demo kernel: linux-3.10.36 author: arvik email: 1216601195@qq.com blog: http://blog.csdn.net/u012819339 *******************************/ #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/types.h> #include <linux/sched.h> #include <net/sock.h> #include <linux/netlink.h> #define NETLINK_USER 22 #define USER_MSG (NETLINK_USER + 1) #define USER_PORT 50 MODULE_LICENSE("GPL"); MODULE_AUTHOR("arvik"); MODULE_DESCRIPTION("netlink_demo"); static struct sock *netlinkfd = NULL; int send_msg(int8_t *pbuf, uint16_t len) { struct sk_buff *nl_skb; struct nlmsghdr *nlh; int ret; nl_skb = nlmsg_new(len, GFP_ATOMIC); if(!nl_skb) { printk("netlink_alloc_skb error\n"); return -1; } nlh = nlmsg_put(nl_skb, 0, 0, USER_MSG, len, 0); if(nlh == NULL) { printk("nlmsg_put() error\n"); nlmsg_free(nl_skb); return -1; } memcpy(nlmsg_data(nlh), pbuf, len); ret = netlink_unicast(netlinkfd, nl_skb, USER_PORT, MSG_DONTWAIT); return ret; } static void recv_cb(struct sk_buff *skb) { struct nlmsghdr *nlh = NULL; void *data = NULL; printk("skb->len:%u\n", skb->len); if(skb->len >= nlmsg_total_size(0)) { nlh = nlmsg_hdr(skb); data = NLMSG_DATA(nlh); if(data) { printk("kernel receive data: %s\n", (int8_t *)data); send_msg(data, nlmsg_len(nlh)); } } } struct netlink_kernel_cfg cfg = { .input = recv_cb, }; static int __init test_netlink_init(void) { printk("init netlink_demo!\n"); netlinkfd = netlink_kernel_create(&init_net, USER_MSG, &cfg); if(!netlinkfd) { printk(KERN_ERR "can not create a netlink socket!\n"); return -1; } printk("netlink demo init ok!\n"); return 0; } static void __exit test_netlink_exit(void) { sock_release(netlinkfd->sk_socket); printk(KERN_DEBUG "netlink exit\n!"); } module_init(test_netlink_init); module_exit(test_netlink_exit);
[/code]
用户层
/******************************* file: u_netlink.c description: netlink demo author: arvik email: 1216601195@qq.com blog: http://blog.csdn.net/u012819339 *******************************/ #include <stdio.h> #include <stdlib.h> #include <sys/socket.h> #include <string.h> #include <linux/netlink.h> #include <stdint.h> #include <unistd.h> #include <errno.h> #define NETLINK_USER 22 #define USER_MSG (NETLINK_USER + 1) #define MSG_LEN 100 #define MAX_PLOAD 100 struct _my_msg { struct nlmsghdr hdr; int8_t data[MSG_LEN]; }; int main(int argc, char **argv) { char *data = "hello kernel"; struct sockaddr_nl local, dest_addr; int skfd; struct nlmsghdr *nlh = NULL; struct _my_msg info; int ret; skfd = socket(AF_NETLINK, SOCK_RAW, USER_MSG); if(skfd == -1) { printf("create socket error...%s\n", strerror(errno)); return -1; } memset(&local, 0, sizeof(local)); local.nl_family = AF_NETLINK; local.nl_pid = 50; local.nl_groups = 0; if(bind(skfd, (struct sockaddr *)&local, sizeof(local)) != 0) { printf("bind() error\n"); close(skfd); return -1; } memset(&dest_addr, 0, sizeof(dest_addr)); dest_addr.nl_family = AF_NETLINK; dest_addr.nl_pid = 0; // to kernel dest_addr.nl_groups = 0; nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PLOAD)); memset(nlh, 0, sizeof(struct nlmsghdr)); nlh->nlmsg_len = NLMSG_SPACE(MAX_PLOAD); nlh->nlmsg_flags = 0; nlh->nlmsg_type = 0; nlh->nlmsg_seq = 0; nlh->nlmsg_pid = local.nl_pid; //self port memcpy(NLMSG_DATA(nlh), data, strlen(data)); ret = sendto(skfd, nlh, nlh->nlmsg_len, 0, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr_nl)); if(!ret) { perror("sendto error1\n"); close(skfd); exit(-1); } printf("wait kernel msg!\n"); memset(&info, 0, sizeof(info)); ret = recvfrom(skfd, &info, sizeof(struct _my_msg), 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr)); if(!ret) { perror("recv form kernel error\n"); close(skfd); exit(-1); } printf("msg receive from kernel:%s\n", info.data); close(skfd); free((void *)nlh); return 0; }
[/code]
用户层:自己的测试程序
/******************************* file: u_netlink.c description: netlink button author: sean email: blog: http://blog.csdn.net/bingyu9875 *******************************/ #include <stdio.h> #include <stdlib.h> #include <sys/socket.h> #include <string.h> #include <linux/netlink.h> #include <stdint.h> #include <unistd.h> #include <errno.h> #define NETLINK_USER 22 #define USER_MSG (NETLINK_USER + 1) #define MSG_LEN 100 #define MAX_PLOAD 100 int main(int argc, char **argv) { int skfd = 0, ret = 0; int button_action = 0; struct nlmsghdr *nlh = NULL; static char buf[4096], cmd[100]; struct sockaddr_nl nls; memset(&nls,0,sizeof(struct sockaddr_nl)); nls.nl_family = AF_NETLINK; nls.nl_pid = getpid(); nls.nl_groups = -1; // skfd = socket(AF_NETLINK, SOCK_RAW, USER_MSG); skfd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); if(skfd == -1) { printf("create socket error...%s\n", strerror(errno)); return -1; } if (bind(skfd, (void *)&nls, sizeof(struct sockaddr_nl))) { printf("Failed to bind hotplug socket: %s\n", strerror(errno)); return -1; } printf("wait kernel msg!\n"); while ( 1 ) { memset(&buf, 0, sizeof(buf)); ret = recv(skfd, buf, sizeof(buf), 0); if(!ret) { perror("recv form kernel error\n"); close(skfd); exit(-1); } int i =0; while ( i < ret ) { int l = strlen(buf + i) + 1; char *e = strstr(&buf[i], "="); if ( e ) { *e = '\0'; printf("msg receive from kernel:%s = %s\n", &buf[i], &e[1]); if ( strcmp(&e[1], "released") == 0 ) { button_action = 1; printf("================ \n"); } if ( strcmp(&buf[i], "SEEN") == 0 && button_action == 1) { printf("-------------- \n"); sprintf(cmd, "/tmp/wps %s", &e[1]); system(cmd); button_action = 0; } } i+=l; } } close(skfd); free((void *)nlh); return 0; }
说明,一定要先加载内核模块,要不然用户层创建套接字会失败!(因为内核模块会注册netlink所使用的协议类型,本例子中协议类型为USER_MSG,即23)
相关文章推荐
- netlink快速使用例程(linux-3.10.36)
- 使用Java语言快速开发Linux GUI的应用
- 使用GTK+和Glade快速开发Linux图形界面
- Linux下使用rsync最快速删除海量文件的方法
- linux使用dd命令快速生成大文件
- linux 内核与用户空间通信之netlink使用方法
- Linux下,使用C/C++编写的一个简单的信号处理例程
- Linux下使用Shell脚本快速创建项目目录模板
- Linux下使用rsync最快速删除海量文件的方法
- 快速实现配置Windows客户端访问在linux平台上所搭建的Samba服务器&&使用swat软件
- 使用 kexec 快速重启 Linux
- 在Linux下使用STL快速入门
- 使用 kexec 快速重启 Linux(转)
- 使用GTK+和Glade快速开发Linux图形界面
- 使用 kexec 快速重启 Linux
- 快速使用linux的历史命令
- 快速使用locate命令查找Linux中的文件
- linux 内核与用户空间通信之netlink使用方法
- linux使用 netlink 添加路由简单代码
- 如何快速估计/估算Linux VPS的月流量使用情况(不用安装MRTG等流量监控软件) - LinuxVPS使用教程 - 国外/美国服务器租用