Generic netlink编程入门
来源:互联网 发布:unity3d运动的模型 编辑:程序博客网 时间:2024/06/06 15:02
通过generic netlink可以实现内核和用户空间的通信,genetlink是通过family来管理的(哈希表),genl_ctrl 是一个特殊的Family, 它是由Generic Netlink自己注册和实现,并用来查询Family列表、管理各个Family的添加、删除等事件的。用户空间先根据family name请求到相应的family ID,而后进行相互沟通。
static struct genl_family genl_ctrl = {
.id = GENL_ID_CTRL,
.name = "nlctrl",
.version = 0x2,
.maxattr = CTRL_ATTR_MAX,
.netnsok = true,
};
static struct genl_ops genl_ctrl_ops = {
.cmd = CTRL_CMD_GETFAMILY,
.doit = ctrl_getfamily,
.dumpit = ctrl_dumpfamily,
.policy = ctrl_policy,
};
.id = GENL_ID_CTRL,
.name = "nlctrl",
.version = 0x2,
.maxattr = CTRL_ATTR_MAX,
.netnsok = true,
};
static struct genl_ops genl_ctrl_ops = {
.cmd = CTRL_CMD_GETFAMILY,
.doit = ctrl_getfamily,
.dumpit = ctrl_dumpfamily,
.policy = ctrl_policy,
};
static const struct nla_policy ctrl_policy[CTRL_ATTR_MAX+1] = {
[CTRL_ATTR_FAMILY_ID] = { .type = NLA_U16 },
[CTRL_ATTR_FAMILY_NAME] = { .type = NLA_NUL_STRING, .len = L_NAMSIZ - 1 },
};
[CTRL_ATTR_FAMILY_ID] = { .type = NLA_U16 },
[CTRL_ATTR_FAMILY_NAME] = { .type = NLA_NUL_STRING, .len = L_NAMSIZ - 1 },
};
genl_kern.c
#include <net/genetlink.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/init.h>/*netlink attributes 可以通过枚举索引找到对应的类型*用户空间应用程序要传递这样的信息*/enum {DOC_EXMPL_A_UNSPEC,DOC_EXMPL_A_MSG,__DOC_EXMPL_A_MAX,};#define DOC_EXMPL_A_MAX (__DOC_EXMPL_A_MAX - 1)/*atribute policy就是定义各个属性的具体类型,参见net/netlink.h*/static struct nla_policy doc_exmpl_genl_policy[DOC_EXMPL_A_MAX + 1] = { [DOC_EXMPL_A_MSG] = {.type = NLA_NUL_STRING},};#define VERSION_NR 1//generic netlink family 定义static struct genl_family doc_exmpl_genl_family = { .id = GENL_ID_GENERATE, .hdrsize = 0, .name = "CONTROL_EXMPL", .version = VERSION_NR, .maxattr = DOC_EXMPL_A_MAX,};/*定义命令类型,用户空间以此来表明需要执行的命令*/enum{DOC_EXMPL_C_UNSPEC,DOC_EXMPL_C_ECHO,__DOC_EXMPL_C_MAX,};#define DOC_EXMPL_C_MAX (__DOC_EXMPL_C_MAX - 1)//echo command handler,接收一个msg并回复int doc_exmpl_echo(struct sk_buff *skb2, struct genl_info *info){ struct nlattr *na; struct sk_buff *skb; int rc; void *msg_hdr; char *data; if(info == NULL) goto error; //对于每个属性,genl_info的域attrs可以索引到具体结构,里面有payload na = info->attrs[DOC_EXMPL_A_MSG]; if(na){ data = (char *)nla_data(na); if(!data) printk("Receive data error!\n"); else printk("Recv:%s\n",data); }else{ printk("No info->attrs %d\n",DOC_EXMPL_A_MSG); } skb = genlmsg_new(NLMSG_GOODSIZE,GFP_KERNEL); if(!skb) goto error; /*构建消息头,函数原型是 genlmsgput(struct sk_buff *,int pid,int seq_number, struct genl_family *,int flags,u8 command_index); */ msg_hdr = genlmsg_put(skb,0,info->snd_seq+1,&doc_exmpl_genl_family, 0,DOC_EXMPL_C_ECHO); if(msg_hdr == NULL){ rc = -ENOMEM; goto error; } //填充具体的netlink attribute:DOC_EXMPL_A_MSG,这是实际要传的数据 rc = nla_put_string(skb,DOC_EXMPL_A_MSG,"Hello World from kernel space!"); if(rc != 0) goto error; genlmsg_end(skb,msg_hdr);//消息构建完成 //单播发送给用户空间的某个进程 rc = genlmsg_unicast(genl_info_net(info),skb,info->snd_pid); if(rc != 0){ printk("Unicast to process:%d failed!\n",info->snd_pid); goto error; } return 0; error: printk("Error occured in doc_echo!\n"); return 0;}//将命令command echo和具体的handler对应起来static struct genl_ops doc_exmpl_genl_ops_echo = { .cmd = DOC_EXMPL_C_ECHO, .flags = 0, .policy = doc_exmpl_genl_policy, .doit = doc_exmpl_echo, .dumpit = NULL,};//内核入口,注册generic netlink family/operationsstatic int __init genKernel_init(void) { int rc; printk("Generic Netlink Example Module inserted.\n"); rc = genl_register_family(&doc_exmpl_genl_family); if (rc != 0) { goto failure; } rc = genl_register_ops(&doc_exmpl_genl_family,&doc_exmpl_genl_ops_echo); if (rc != 0) { printk("Register ops: %i\n",rc); genl_unregister_family(&doc_exmpl_genl_family); goto failure; } return 0; failure: printk("Error occured while inserting generic netlink example module\n"); return -1;}static void __exit genKernel_exit(void) { int ret; printk("Generic Netlink Example Module unloaded.\n"); ret = genl_unregister_ops(&doc_exmpl_genl_family,&doc_exmpl_genl_ops_echo); if(ret != 0) { printk("Unregister ops failed: %i\n",ret); return; } ret = genl_unregister_family(&doc_exmpl_genl_family); if(ret !=0) { printk("Unregister family failed:%i\n",ret); }}module_init(genKernel_init);module_exit(genKernel_exit);MODULE_LICENSE("GPL");
genl_user.c
#include <stdio.h>#include <stdlib.h>#include <errno.h>#include <unistd.h>#include <poll.h>#include <string.h>#include <fcntl.h>#include <sys/types.h>#include <sys/stat.h>#include <sys/socket.h>#include <sys/types.h>#include <signal.h>#include <linux/genetlink.h>//宏定义:根据generic netlink msg的具体构造定位#define GENLMSG_DATA(glh) ((void *)(NLMSG_DATA(glh) + GENL_HDRLEN))#define GENLMSG_PAYLOAD(glh) (NLMSG_PAYLOAD(glh, 0) - GENL_HDRLEN)#define NLA_DATA(na) ((void *)((char*)(na) + NLA_HDRLEN))#define MESSAGE_TO_KERNEL "Hello World from user space!"int nl_fd;struct sockaddr_nl nl_address;int nl_family_id;int len;struct nlattr *nl_na;struct { // struct nlmsghdr n; struct genlmsghdr g; char buf[256];} nl_request_msg, nl_response_msg;int main(void) { nl_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); if (nl_fd < 0) { perror("socket()"); return -1; } memset(&nl_address, 0, sizeof(nl_address)); nl_address.nl_family = AF_NETLINK; nl_address.nl_groups = 0; if (bind(nl_fd, (struct sockaddr *) &nl_address, sizeof(nl_address)) < 0) { perror("bind()"); close(nl_fd); return -1; } nl_request_msg.n.nlmsg_type = GENL_ID_CTRL;//这是内核中genl_ctl的id nl_request_msg.n.nlmsg_flags = NLM_F_REQUEST; nl_request_msg.n.nlmsg_seq = 0; nl_request_msg.n.nlmsg_pid = getpid(); nl_request_msg.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); //Populate the payload's "family header" : which in our case is genlmsghdr nl_request_msg.g.cmd = CTRL_CMD_GETFAMILY; nl_request_msg.g.version = 0x1; //Populate the payload's "netlink attributes" nl_na = (struct nlattr *) GENLMSG_DATA(&nl_request_msg);//其实就相当于在nl_request_msg 的buf域中构造一个nla nl_na->nla_type = CTRL_ATTR_FAMILY_NAME; nl_na->nla_len = strlen("CONTROL_EXMPL") + 1 + NLA_HDRLEN; strcpy(NLA_DATA(nl_na), "CONTROL_EXMPL"); //Family name length can be upto 16 chars including \0 nl_request_msg.n.nlmsg_len += NLMSG_ALIGN(nl_na->nla_len); memset(&nl_address, 0, sizeof(nl_address)); nl_address.nl_family = AF_NETLINK; len= sendto(nl_fd, (char *) &nl_request_msg, nl_request_msg.n.nlmsg_len, 0, (struct sockaddr *) &nl_address, sizeof(nl_address)); if (len != nl_request_msg.n.nlmsg_len) { perror("sendto()"); close(nl_fd); return -1; } len= recv(nl_fd, &nl_response_msg, sizeof(nl_response_msg), 0); if (len < 0) { perror("recv()"); return -1; } if (!NLMSG_OK((&nl_response_msg.n), len)) { fprintf(stderr, "family ID request : invalid message\n"); return -1; } if (nl_response_msg.n.nlmsg_type == NLMSG_ERROR) { //error fprintf(stderr, "family ID request : receive error\n"); return -1; } //解析出attribute中的family id nl_na = (struct nlattr *) GENLMSG_DATA(&nl_response_msg); nl_na = (struct nlattr *) ((char *) nl_na + NLA_ALIGN(nl_na->nla_len)); if (nl_na->nla_type == CTRL_ATTR_FAMILY_ID) { nl_family_id = *(__u16 *) NLA_DATA(nl_na);//第一次通信就是为了得到需要的family ID } memset(&nl_request_msg, 0, sizeof(nl_request_msg)); memset(&nl_response_msg, 0, sizeof(nl_response_msg)); nl_request_msg.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); nl_request_msg.n.nlmsg_type = nl_family_id; nl_request_msg.n.nlmsg_flags = NLM_F_REQUEST; nl_request_msg.n.nlmsg_seq = 60; nl_request_msg.n.nlmsg_pid = getpid(); nl_request_msg.g.cmd = 1; //corresponds to DOC_EXMPL_C_ECHO; nl_na = (struct nlattr *) GENLMSG_DATA(&nl_request_msg); nl_na->nla_type = 1; // corresponds to DOC_EXMPL_A_MSG nl_na->nla_len = sizeof(MESSAGE_TO_KERNEL)+NLA_HDRLEN; //Message length memcpy(NLA_DATA(nl_na), MESSAGE_TO_KERNEL, sizeof(MESSAGE_TO_KERNEL)); nl_request_msg.n.nlmsg_len += NLMSG_ALIGN(nl_na->nla_len); memset(&nl_address, 0, sizeof(nl_address)); nl_address.nl_family = AF_NETLINK; len = sendto(nl_fd, (char *) &nl_request_msg, nl_request_msg.n.nlmsg_len, 0, (struct sockaddr *) &nl_address, sizeof(nl_address)); if (len != nl_request_msg.n.nlmsg_len) { perror("sendto()"); close(nl_fd); return -1; } printf("Sent to kernel: %s\n",MESSAGE_TO_KERNEL); len = recv(nl_fd, &nl_response_msg, sizeof(nl_response_msg), 0); if (len < 0) { perror("recv()"); return -1; } //异常处理 if (nl_response_msg.n.nlmsg_type == NLMSG_ERROR) { //Error printf("Error while receiving reply from kernel: NACK Received\n"); close(nl_fd); return -1; } if (len < 0) { printf("Error while receiving reply from kernel\n"); close(nl_fd); return -1; } if (!NLMSG_OK((&nl_response_msg.n), len)) { printf("Error while receiving reply from kernel: Invalid Message\n"); close(nl_fd); return -1; } //解析收到的来自内核的reply len = GENLMSG_PAYLOAD(&nl_response_msg.n); nl_na = (struct nlattr *) GENLMSG_DATA(&nl_response_msg); printf("Kernel replied: %s\n",(char *)NLA_DATA(nl_na)); close(nl_fd); return 0;}
参考: 1. http://people.ee.ethz.ch/~arkeller/linux/multi/kernel_user_space_howto-3.html
1 0
- Generic netlink编程入门
- Generic netlink编程入门
- Generic Netlink详解
- Generic Netlink详解
- 二 generic netlink
- Generic Netlink分析和使用
- netlink 编程
- netlink编程
- netlink 编程
- Linux Generic Netlink 设计与实现
- Generic Netlink(genl)介绍与例子
- netlink编程注意事项
- netlink编程注意事项
- netlink编程注意事项
- netlink 编程介绍
- netlink 编程介绍
- netlink 编程介绍
- netlink 编程介绍
- hdu 3746 Cyclic Nacklace
- 同步GET方法下载图片
- 神网卡黑苹果PCI网卡
- jsoncpp在linux编译
- hashmap 工作原理
- Generic netlink编程入门
- 浅析Oralce的数据库优化
- cvLoadImage,cvCloneImage的内存泄露问题
- Qt实例 1_3 各类位置信息
- Codeforces 394D Physical Education and Buns
- MAC OS
- 文件夹取消SVN关联
- 用mysql数据库进行数据插入成乱码
- PHP Notice: Undefined index: ... 问题的解决方法: