generic netlink 编程快速入门

目录

 

一、generic netlink 消息结构

二、接口搭建过程

kernel端程序的准备

1、定义想要传送的消息种类

2、定义一组命令

3、为每个命令定义一个响应函数

4、将命令与相应函数关联起来

5、创建一个命令族

6、向内核注册新建的命令族

7、将关联好的命令和响应函数绑定(注册)到命令族上

用户端程序的使用:

1、新建一个套接字,并绑定上 netlink address

2、向 GENL_ID_CRTL 询问想通信的命令族的 id

3、等待接收 GENL_ID_CRTL 的返回的查询结果

4、拆分读取接收到的信息

5、与想要通信的命令族通信

三、程序示例

用户态程序

内核态程序

 


一、generic netlink 消息结构

消息以流的形式在程序之间进行传递,一个流中可能包含多个消息。

对于每个消息消息来说,为了便于维护和方便使用,还需要一些有关记录消息的信息。一个 netlink message 结构如下:

其中 nlmsghdr 结构里记录了该条 netlink message 的如下信息:

struct nlmsghdr {
	__u32		nlmsg_len;	//该 netlink message 的长度(包括 nlmsghdr 部分本身)
	__u16		nlmsg_type;	//该 netlink message 的种类
	__u16		nlmsg_flags; //一个附加的标记
	__u32		nlmsg_seq;	//该 netlink message 在消息流中的序列号
	__u32		nlmsg_pid;	//发送该 netlink message 的进程的进程号 pid
};

为了使数据对齐,在 payload 两端可能会塞入了一些填充字段。netlink message 的 payload 结构如下:

这个结构称作 family。进程间通信的目的通常是希望信息接收方能够执行某些动作,而很多命令都是围绕同一个事件的(如对某个表的增、删、改、查)。因此为了便于管理,netlink 通信协议将这些相关的一套命令和其对应的操作组织起来,归于一个family管辖,每个family都有个独有的名字,该名字需公示出来,以便其他程序与该 family 通信,当 family 在内核完成注册后,内核会给其分配一个独有的 id。如果用户需要结构化的信息,可以在 netlink message 的 payload 中添加可自选的信息头 user specific header。剩下的 attributes 结构如下:

每个 attribute 即是一个不可再分的最基本信息单元,结构如下:

其中 nlattr 是管理 payload 的信息头,记录了 payload 的以下信息:

struct nlattr {
	__u16           nla_len;//attribute 长度,包括 nlattr 本身
	__u16           nla_type;//attribute 的类型
};

payload 部分即是想要传递的信息了,如传递一句话 “ hello world ”。为了对齐,payload 两端可能会塞入 pad。

netlink message 协议使用的信息结构即是以上这些,但对着使用越来越多,内核可以用来分配给 family 的 id 资源开始紧张了。因此 generic netlink 协议在 netlink 协议上又做了一些拓展,即在 netlink message 的 payload 上又做了一层封装:

该结构即为 generic netlink message。该信息头 genlmsghdr 中记录如下内容。

struct genlmsghdr {
	__u8	cmd;        //命令号
	__u8	version;    //命令版本号
	__u16	reserved;   //保留字段
};

故最终 generic netlink message 的结构如下:

二、接口搭建过程

kernel端程序的准备

1、定义想要传送的消息种类

/* generic netlink message attributes */
enum {
      MY_ATTR_UNSPEC,
      MY_ATTR_MSG,
      __MY_ATTR_MAX,
};
#define MY_ATTR_MAX (__MY_ATTR_MAX - 1)

你可能有多种消息需要传递,使用枚举类型来管理消息种类是个不错的办法。这里实际上只定义了一个消息种类,即 MY_ATTR_MSG。 根据枚举类型的特点,在枚举体最后定义的 __MY_ATTR_MAX 实际上表示了该属性集的大小,而 MY_ATTR_MAX 则表示了该属性集中最后一类属性(即 MY_MTTR_MSG)的编号 1。 

(在 linux 内核中大量的枚举体的首个元素名均是以 UNSPEC 结尾,在这里 MY_ATTR_UNSPEC 作为该属性集的“一般属性”,可能只是用来对应一些“空操作”,而并不特指哪一消息类型。——作者猜测)

2、定义一组命令

/* commands 定义命令类型,用户空间以此来表明需要执行的命令 */
enum {
    MY_CMD_UNSPEC, 
    MY_CMD_1,      //命令
    __MY_CMD_MAX,  //表示命令集的大小,并不是命令
};
#define MY_CMD_MAX (__MY_CMD_MAX - 1)

与消息属性的定义类似,这里同样使用了枚举类型来定义命令集,但其实这里只定义了一个命令,即 MY_CMD_1。根据枚举类型的特点,在枚举体最后定义的 __MY_CMD_MAX 实际上表示了该命令集的大小,而 MY_CMD_MAX 则表示了该命令集中最后一个命令(即 MY_CMD_1)的编号 1。 

3、为每个命令定义一个响应函数

generic netlink协议要求命令相应函数的类型是:

static int my_callback_function(struct sk_buff *skb, struct genl_info *info);

4、将命令与相应函数关联起来

static struct genl_ops my_cmd_ops = {
        .cmd = MY_CMD_1, //命令
        .flags = 0,
        .policy = my_cmd_policy,
        .doit = my_callback_function, //响应函数
        .dumpit = NULL,
};

通过定义一个 genl_ops, 我们可以将一个命令和其对应的响应函数关联起来。成员 policy 指明了该响应函数对其能够处理的信息的要求。具体如下:

static struct nla_policy my_cmd_policy[MY_ATTRIBUTE_MAX + 1] = {
      [MY_ATTRIBUTE_MSG] = { .type = NLA_NUL_STRING },
};

这里使用了比较难懂的语法,先来看一下 struct nla_policy 的定义:

struct nla_policy {
	u16		type;
	u16		len;
};

my_cmd_policy 是一个 nal_policy 结构体数组,数组长度为2(即 MY_ATTRIBUTE_MAX+1) ,即前文定义的 generic netlink message 属性集的长度(my_cmd_policy[0] 对应 MY_ATTRIBUTE_UNSPEC;my_cmd_policy[1] 对应 MY_ATTRIBUTE_MSG)。在大括号中将 my_cmd_policy[1] 的 type 属性赋值为 NLA_NUL_STRING,该宏的意思是除去NULL外的最大字符串长度。

5、创建一个命令族


/* family definition */
static struct genl_family my_cmd_family = {
      .id = GENL_ID_GENERATE,   //
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值