ImageVerifierCode 换一换
格式:DOCX , 页数:18 ,大小:259.66KB ,
资源ID:11890691      下载积分:10 金币
快捷注册下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

开通VIP
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.zixin.com.cn/docdown/11890691.html】到电脑端继续下载(重复下载【60天内】不扣币)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

开通VIP折扣优惠下载文档

            查看会员权益                  [ 下载后找不到文档?]

填表反馈(24小时):  下载求助     关注领币    退款申请

开具发票请登录PC端进行申请

   平台协调中心        【在线客服】        免费申请共赢上传

权利声明

1、咨信平台为文档C2C交易模式,即用户上传的文档直接被用户下载,收益归上传人(含作者)所有;本站仅是提供信息存储空间和展示预览,仅对用户上传内容的表现方式做保护处理,对上载内容不做任何修改或编辑。所展示的作品文档包括内容和图片全部来源于网络用户和作者上传投稿,我们不确定上传用户享有完全著作权,根据《信息网络传播权保护条例》,如果侵犯了您的版权、权益或隐私,请联系我们,核实后会尽快下架及时删除,并可随时和客服了解处理情况,尊重保护知识产权我们共同努力。
2、文档的总页数、文档格式和文档大小以系统显示为准(内容中显示的页数不一定正确),网站客服只以系统显示的页数、文件格式、文档大小作为仲裁依据,个别因单元格分列造成显示页码不一将协商解决,平台无法对文档的真实性、完整性、权威性、准确性、专业性及其观点立场做任何保证或承诺,下载前须认真查看,确认无误后再购买,务必慎重购买;若有违法违纪将进行移交司法处理,若涉侵权平台将进行基本处罚并下架。
3、本站所有内容均由用户上传,付费前请自行鉴别,如您付费,意味着您已接受本站规则且自行承担风险,本站不进行额外附加服务,虚拟产品一经售出概不退款(未进行购买下载可退充值款),文档一经付费(服务费)、不意味着购买了该文档的版权,仅供个人/单位学习、研究之用,不得用于商业用途,未经授权,严禁复制、发行、汇编、翻译或者网络传播等,侵权必究。
4、如你看到网页展示的文档有www.zixin.com.cn水印,是因预览和防盗链等技术需要对页面进行转换压缩成图而已,我们并不对上传的文档进行任何编辑或修改,文档下载后都不会有水印标识(原文档上传前个别存留的除外),下载后原文更清晰;试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓;PPT和DOC文档可被视为“模板”,允许上传人保留章节、目录结构的情况下删减部份的内容;PDF文档不管是原文档转换或图片扫描而得,本站不作要求视为允许,下载前可先查看【教您几个在下载文档中可以更好的避免被坑】。
5、本文档所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用;网站提供的党政主题相关内容(国旗、国徽、党徽--等)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
6、文档遇到问题,请及时联系平台进行协调解决,联系【微信客服】、【QQ客服】,若有其他问题请点击或扫码反馈【服务填表】;文档侵犯商业秘密、侵犯著作权、侵犯人身权等,请点击“【版权申诉】”,意见反馈和侵权处理邮箱:1219186828@qq.com;也可以拔打客服电话:0574-28810668;投诉电话:18658249818。

注意事项

本文(Generic-Netlink详解.docx)为本站上传会员【仙人****88】主动上传,咨信网仅是提供信息存储空间和展示预览,仅对用户上传内容的表现方式做保护处理,对上载内容不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知咨信网(发送邮件至1219186828@qq.com、拔打电话4009-655-100或【 微信客服】、【 QQ客服】),核实后会尽快下架及时删除,并可随时和客服了解处理情况,尊重保护知识产权我们共同努力。
温馨提示:如果因为网速或其他原因下载失败请重新下载,重复下载【60天内】不扣币。 服务填表

Generic-Netlink详解.docx

1、  Generic Netlink详解 分类: linux2013-06-13 20:53 1454人阅读 评论(2) 收藏 举报 内核linuxnetlink netlink socket是一种用于用户态进程和内核态进程之间的通信机制。它通过为内核模块提供一组特殊的API,并为用户程序提供了一组标准的socket接口的方式,实现了全双工的通讯连接。 Netlink的特点: · 双向传输,异步通信 · 用户空间中使用标准socket API · 内核空间中使用专门的API · 支持多播 · 可由内核端发起通信 · 支持32种协议类型 netlink仅支持32种协议类型,

2、这在实际应用中可能并不足够。因此产生了generic netlink(以下简称为genl)。 generic netlink支持1023个子协议号,弥补了netlink协议类型较少的缺陷。支持协议号自动分配。它基于netlink,但是在内核中,generic netlink的接口与netlink并不相同。 1. Generic Netlink框架概述 图1表示了Generic Netlink框架。Kernel socket API向用户空间和内核空间分别提供接口。 Netlink子系统(1)是所有genl通信的基础。Netlink子系统中收到的所有Generic类型的netlink数据

3、都被送到genl总线(2)上;从内核发出的数据也经由genl总线送至netlink子系统,再打包送至用户空间。 Generic Netlink控制器(4)作为内核的一部分,负责动态地分配genl通道(即genl family id),并管理genl任务。genl控制器是一个特殊的genl内核用户,它负责监听genl bus上的通信通道。genl通信建立在一系列的通信通道的基础上,每个genl family对应多个通道,这些通道由genl控制器动态分配。 +---------------------+ +---------------------+ | (

4、3) application "A" | | (3) application "B" | +------+--------------+ +--------------+------+ | | \ / \ / |

5、 | +-------+--------------------------------+-------+ | : : | user-space =====+ : (5) Kernel socket API : +================ | : : | kernel-space

6、 +--------+-------------------------------+-------+ | | +-----+-------------------------------+----+ | (1) Netlink subsystem | +---------------------+--------------------+

7、 | +---------------------+--------------------+ | (2) Generic Netlink bus | +--+--------------------------+-------+----+ | | | +-------+---------+ | | | (4) Contr

8、oller | / \ +-----------------+ / \ | | +------------------+--+ +--+------------------+ | (3) kernel user "X" | | (3) kernel user "Y" |

9、 +---------------------+ +---------------------+ 图1:generic netlink框架 2 Generic Netlink相关结构体 2.1 genl family Generic Netlink是基于客户端-服务端模型的通信机制。服务端注册family(family是对genl服务的各项定义的集合)。控制器和客户端都通过已注册的信息与服务端通信。 genl family的结构体如下: struct genl_family { unsigned int id;

10、unsigned int hdrsize; char name[GENL_NAMSIZ]; unsigned int version; unsigned int maxattr; struct nlattr ** attrbuf; struct list_head ops_list; struct list_head family_list; }; 对此结构体

11、元素具体解释如下: * id: family id。当新注册一个family的时候,应该用GENL_ID_GENERATE宏(0x0),表示请控制器自动为family分配的一个id。0x10保留供genl控制器使用。 * hdrsize: 用户自定议头部长度。即图2中User Msg的长度。如果没有用户自定义头部,这个值被赋为0。 * version: 版本号,一般填1即可。 * name: family名,要求不同的family使用不同的名字。以便控制器进行正确的查找。 * maxattr:genl使用netlink标准的attr来传输数据。此字段定义了最大attr类型数。

12、注意:不是一次传输多少个attr,而是一共有多少种attr,因此,这个值可以被设为0,为0代表不区分所收到的数据的attr type)。在接收数据时,可以根据attr type,获得指定的attr type的数据在整体数据中的位置。 * struct nlattr **attrbuf * struct list_head ops_list * struct list_head family_list 以上的三个字段为私有字段,由系统自动配置,开发者不需要做配置。 图2 genl报文与linux中各变量的对应关系 图3 genl报文格式 2.2 genl_ops

13、结构体 struct genl_ops { u8 cmd; unsigned int flags; struct nla_policy *policy; int (*doit)(struct sk_buff *skb, struct genl_info *info); int (*dumpi

14、t)(struct sk_buff *skb, struct netlink_callback *cb); struct list_head ops_list; }; ·   cmd: 命令名。用于识别各genl_ops ·   flag: 各种设置属性,以“或”连接。在需要admin特权级别时,使用GENL_ADMIN_PERM ·   policy:定义了attr规则。如果此指针非空,genl在触发事件处理程序之前,会使用这个字段来对帧中的attr做校验(见nlms

15、g_parse函数)。该字段可以为空,表示在触发事件处理程序之前,不做校验。         policy是一个struct nla_policy的数组。struct nla_policy结构体表示如下: struct nla_policy { u16 type; u16 len; };         其中,type字段表示attr中的数据类型,可被配置为:        NLA_UNSPEC--未定义        NLA_U8,

16、 NLA_U16, NLA_U32, NLA_U64为8bits, 16bits, 32bits, 64bits的无符号整型        NLA_STRING--字符串        NLA_NUL_STRING--空终止符字符串        NLA_NESTED--attr流        len字段的意思是:如果在type字段配置的是字符串有关的值,要把len设置为字符串的最大长度(不包含结尾的'\0')。如果type字段未设置或被设置为NLA_UNSPEC,那么这里要设置为attr的payload部分的长度。 · doit:这是一个回调函数。在generic net

17、link收到数据时触发,运行在进程上下文。         doit传入两个参数,skb为触发此回调函数的socket buffer。第二个参数是一个genl_info结构体,定义如下: struct genl_info { u32 snd_seq; u32 snd_pid; struct nlmsghdr * nlhdr; struct genlmsgh

18、dr * genlhdr; void * userhdr; struct nlattr ** attrs; };                  * snd_seq:发送序号                                   * snd_pid:发送客户端的PID                                  * nlhdr:netlink header的指针                              

19、     * genlmsghdr:genl头部的指针(即family头部)                                   * userhdr:用户自定义头部指针                                  * attrs:attrs,如果定义了genl_ops->policy,这里的attrs是被policy过滤以后的结果。 在完成了操作以后,如果执行正确,返回0;否则,返回一个负数。(一定要有返回值,不能不返回)负数的返回值会触发NLMSG_ERROR消息。当genl_ops的flag标志被添加了NLMSG_ERROR时,即使doit返回0

20、也会触发NLMSG_ERROR消息。 · dumpit 这是一个回调函数,当genl_ops的flag标志被添加了NLM_F_DUMP以后,每次收到genl消息即会回触发这个函数。dumpit与doit的区别是:dumpit的第一个参数skb不会携带从客户端发来的数据。相反地,开发者应该在skb中填入需要传给客户端的数据,然后,并skb的数据长度(可以用skb->len)return。skb中携带的数据会被自动送到客户端。只要dumpit的返回值大于0,dumpit函数就会再次被调用,并被要求在skb中填入数据。当服务端没有数据要传给客户端时,dumpit要返回0。如果函数中出错,要求返

21、回一个负值。关于doit和dumpit的触发过程,可以查看源码中的genl_rcv_msg函数。 · ops_list 为私有字段,由系统自动配置,开发者不需要做配置。 3 Generic Netlink服务端(内核)初始化 初始化Generic Netlink的过程分为以下四步:定义family,定义operation,注册family,注册operation。下面通过一个简单例子来说明如何完成Generic Netlink的初始化。我们首先创建一个genl_family结构体的实例。我们在这里定义一个名为"DOC_EXMPL"的family /* attribute type

22、 */ enum { DOC_EXMPL_A_UNSPEC, DOC_EXMPL_A_MSG, __DOC_EXMPL_A_MAX, }; #define DOC_EXMPL_A_MAX (__DOC_EXMPL_A_MAX - 1) /* family definition */ static struct genl_family doc_exmpl_gnl_family = { .id = GENL_ID_GENERATE, .hdrsize = 0,

23、 .name = "DOC_EXMPL", .version = 1, .maxattr = DOC_EXMPL_A_MAX, }; 以上,我们定义了一个仅有一种attribuste type的family。.id被配置为GENL_ID_GENERATE,指示genl控制器自动分配一个id。 第二步为family创建operations。我们至少要创建一个genl_ops结构体的实例。 /* doit handler */ int genl_recv_doit(struct sk_buff *skb, stru

24、ct genl_info *info) { /* message handling code goes here; return 0 on success, negative * values on failure */ } /* attribute policy */ static struct nla_policy doc_exmpl_genl_policy = [DOC_EXMPL_A_MAX + 1] = { [DOC_EXMPL_A_MSG] = { .type = NLA_NUL_STRING

25、}, } /* commands */ enum { DOC_EXMPL_C_UNSPEC, DOC_EXMPL_C_ECHO, __DOC_EXMPL_C_ECHO, }; #define DOC_EXMPL_C_MAX (__DOC_EXMPL_C_MAX - 1) /* operation definition */ struct genl_ops doc_exmpl_gnl_ops_echo = { .cmd = DOC_EXMPL_C_ECHO,

26、 .flags = 0, .policy = doc_exmpl_genl_policy, .doit = genl_recv_doit, .dumpit = NULL, } 这里,我们把attribute policy设为NLA_NUL_STRING,表示attr中数据的属性为无NULL结尾的字符串。控制器在收到数据时会自动完成这一类型检查。 我们定义一个operation,它的id为DOC_EXMPL_C_ECHO,把上述的policy配置给它。一旦本family的genl消息在被总到genl总线上,doi

27、t函数(doc_exmpl_echo)会被调用。 接下来两步是注册family和注册operations。 genl_register_family(&doc_exmpl_gnl_family); genl_register_ops(&doc_exmpl_gnl_family, &doc_exmpl_gnl_ops_echo); 在完成genl操作后,记对完成对family的注销操作。 genl_unregister_family(&doc_exmpl_gnl_family); 4 Generic Netlink客户端(用户空间)初始化 Gener

28、ic Netlink在用户空间的初始化和通常的socket通信一致。大致分为两步,创建socket,把socket绑定到地址上(bind)。 下面也通过一个例子简要说明一下用户空间genl初始化的过程。 struct sockaddr_nl saddr; int sock; sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); if (sock < 0) { return -1; } memset(&saddr, 0,

29、sizeof(saddr)); saddr.nl_family = AF_NETLINK; saddr.nl_pid = getpid(); if (bind(sock, (struct sockaddr*)&saddr, sizeof(saddr)) < 0) { printf("bind fail!\n"); close(*p_sock); return -1; } 上述代码中,我们先创建一个socket,注意,第一个参数必须为AF_NETLINK 或 PF_NETLINK,表示创建

30、netlink socket,第二个参数必须是SOCK_RAW或SOCK_DGRAM, 第三个参数指定netlink协议类型,我们要使用generic netlink,那么就要将其设置为:NETLINK_GENERIC。 接下来,对于genl不可缺少的一步就是获取family id。family id是服务端注册family时,由控制器自动分配的。此时客户端尚不知道family id为多少,因此需要向客户端请求family id。 下面是一段获取family id的函数 static int genl_get_family_id(int sd, char *family_

31、name) { msgtemplate_t ans; int id, rc; struct nlattr *na; int rep_len; rc = genl_send_msg(sd, GENL_ID_CTRL, 0, CTRL_CMD_GETFAMILY, 1, CTRL_ATTR_FAMILY_NAME, (void *)family_name, strlen(family_name)+1); rep_len = recv(sd, &a

32、ns, sizeof(ans), 0); if (rep_len < 0) { return 0; } if (ans.n.nlmsg_type == NLMSG_ERROR || !NLMSG_OK((&ans.n), rep_len)) { return 0; } na = (struct nlattr *) GENLMSG_DATA(&ans); na = (struct nlattr *) ((char *) na + NLA_ALIGN(na->nla_len)); if (

33、na->nla_type == CTRL_ATTR_FAMILY_ID) { id = *(__u16 *) NLA_DATA(na); } else { id = 0; } return id; } 在这个函数中,调用genl_send_msg(这个函数会在下文中介绍并给出源码)发送请求family id的消息,并调用recv接收服务端的反馈消息。这个消息中即包含了family id。 这个函数的第一个参数是已创建好的socket。第二个参数是family name,注意这里family name需要

34、与服务端注册famile时的name字段一致。该函数返回值即是family id以下是一个调用示例。 int fid = genl_get_family_id(sock, "DOC_EXMPL"); 5 Generic Netlink通信 这一节对如何使用Generic Netlink完成内核空间与用户空间的通信做介绍。并把我的示例代码贡献出来。 示例代码呈现了内核(服务端)和用户空间(客户端)收发数据的过程。 5.1 内核发送数据 以下是内核端发送数据的源码。在genl_msg_send_to_user中,调用genl_msg_prepare_usr_msg和genl_m

35、sg_mk_usr_msg来准备socket buffer,为数据加上各种数据头(参考图2)。genlmsg_end把整个数据打包完成,通过genlmsg_unicast完成单播发送。 /** * genl_msg_send_to_user - 通过generic netlink发送数据到netlink * * @data: 发送数据缓存 * @len: 数据长度 单位:byte * @pid: 发送到的客户端pid 这个pid要从用户空间发来数据触发的doit中的info->snd_pid参数获得 * * return: * 0: 成功 *

36、 -1: 失败 */ int genl_msg_send_to_user(void *data, int len, pid_t pid) { struct sk_buff *skb; size_t size; void *head; int rc; size = nla_total_size(len); /* total length of attribute including padding */ rc = genl_msg_prepare_usr_msg(DOC_EXMPL_C_ECHO, size,

37、 pid, &skb); if (rc) { return rc; } rc = genl_msg_mk_usr_msg(skb, DOC_EXMPL_A_MSG, data, len); if (rc) { kfree_skb(skb); return rc; } head = genlmsg_data(nlmsg_data(nlmsg_hdr(skb))); rc = genlmsg_end(skb, head); if (rc < 0)

38、 { kfree_skb(skb); return rc; } rc = genlmsg_unicast(&init_net, skb, pid); if (rc < 0) { return rc; } return 0; } static inline int genl_msg_mk_usr_msg(struct sk_buff *skb, int type, void *data, int len) { int rc; /* add a ne

39、tlink attribute to a socket buffer */ if ((rc = nla_put(skb, type, len, data)) != 0) { return rc; } return 0; } static inline int genl_msg_prepare_usr_msg(u8 cmd, size_t size, pid_t pid, struct sk_buff **skbp) { struct sk_buff *skb; /* create a new netlink ms

40、g */ skb = genlmsg_new(size, GFP_KERNEL); if (skb == NULL) { return -ENOMEM; } /* Add a new netlink message to an skb */ genlmsg_put(skb, pid, 0, &genl_family, 0, cmd); *skbp = skb; return 0; } 5.2 用户空间接收数据 客户端调用通用的recv函数即可完成从内核来的数据的接收。需要注意的是,接收

41、到的数据包含几级的header(图3),我们需要准确地定位到我们所需数据的位置。 当没有用户自定义头部(即图3中的User Msg,在注册family时把hdrsize置0)时,可以构建这样的数据结构用于接收数据。这样,收到的数据中的netlink header和genl header就被很容易地剥离开来。 typedef struct msgtemplate { struct nlmsghdr n; struct genlmsghdr g; char data[MAX_MSG_SIZE]; } msgtemplate_t; 下面是

42、客户端接收数据函数的源码: #define GENLMSG_DATA(glh) ((void *)(NLMSG_DATA(glh) + GENL_HDRLEN)) #define NLA_DATA(na) ((void *)((char *)(na) + NLA_HDRLEN)) void genl_rcv_msg(int fid, int sock, char **string) { int ret; struct msgtemplate msg; struct nlattr *na; ret

43、 recv(sock, &msg, sizeof(msg), 0); if (ret < 0) { return; } //printf("received length %d\n", ret); if (msg.n.nlmsg_type == NLMSG_ERROR || !NLMSG_OK((&msg.n), ret)) { return; } if (msg.n.nlmsg_type == fid && fid != 0) { na = (struct nlattr *)

44、GENLMSG_DATA(&msg); *string = (char *)NLA_DATA(na); } } 以上函数中,第一个参数为family id,第二个参数为socket,第三个参数为待接收数据的buffer。 5.3 用户空间发送数据 客户端发送数据简单地说就是调用通用的socket API---sendto来发送数据 /** * genl_send_msg - 通过generic netlink给内核发送数据 * * @sd: 客户端socket * @nlmsg_type: family_id *

45、@nlmsg_pid: 客户端pid * @genl_cmd: 命令类型 * @genl_version: genl版本号 * @nla_type: netlink attr类型 * @nla_data: 发送的数据 * @nla_len: 发送数据长度 * * return: * 0: 成功 * -1: 失败 */ int genl_send_msg(int sd, u_int16_t nlmsg_type, u_int32_t nlmsg_pid, u_int8_t genl_cmd, u_int8_t genl

46、version, u_int16_t nla_type, void *nla_data, int nla_len) { struct nlattr *na; struct sockaddr_nl nladdr; int r, buflen; char *buf; msgtemplate_t msg; if (nlmsg_type == 0) { return 0; } msg.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);

47、 msg.n.nlmsg_type = nlmsg_type; msg.n.nlmsg_flags = NLM_F_REQUEST; msg.n.nlmsg_seq = 0; /* * nlmsg_pid是发送进程的端口号。 * Linux内核不关心这个字段,仅用于跟踪消息。 */ msg.n.nlmsg_pid = nlmsg_pid; msg.g.cmd = genl_cmd; msg.g.version = genl_version; na = (struct nlattr *)

48、GENLMSG_DATA(&msg); na->nla_type = nla_type; na->nla_len = nla_len + 1 + NLA_HDRLEN; memcpy(NLA_DATA(na), nla_data, nla_len); msg.n.nlmsg_len += NLMSG_ALIGN(na->nla_len); buf = (char *) &msg; buflen = msg.n.nlmsg_len ; memset(&nladdr, 0, sizeof(nladdr)); nla

49、ddr.nl_family = AF_NETLINK; while ((r = sendto(sd, buf, buflen, 0, (struct sockaddr *) &nladdr , sizeof(nladdr))) < buflen) { if (r > 0) { buf += r; buflen -= r; } else if (errno != EAGAIN) { return -1; } }

50、 return 0; } 5.4 内核接收数据 内核端一旦收到generic netlink数据,会触发doit函数运行(上文第3节有提及doit的初始化方法)。 doit传入两个参数,skb即是接收到的数据,info包含了Genl消息的一些常用指针。这两个结构体字段详见内核源码。 skb收到的数据还包括了多层的包头,以下程序中的nlmsg_hdr,nlmsg_data,genlmsg_data,nla_data即是把这些包头层层剥开,para->string指向的数据即是用用户空间传来的“纯数据”。 int genl_recv_doit(str

移动网页_全站_页脚广告1

关于我们      便捷服务       自信AI       AI导航        抽奖活动

©2010-2026 宁波自信网络信息技术有限公司  版权所有

客服电话:0574-28810668  投诉电话:18658249818

gongan.png浙公网安备33021202000488号   

icp.png浙ICP备2021020529号-1  |  浙B2-20240490  

关注我们 :微信公众号    抖音    微博    LOFTER 

客服