文章目录
系统中有已经有很多具体的Netlink协议了,不过它们都是针对某个子系统的,功能已经比较固定,如果我们需要扩展新的Netlink协议,那么就需要对内核进行侵入式修改(最起码要扩展MAX_LINKS),为此,Netlink框架还提供了一个通用的Netlink协议: Generic Netlink。通过它,内核模块可以在无需扩充协议的基础上使用Netlink机制。
这篇笔记分析了内核Generic Netlink核心部分的实现,官方参考资料见这里。
文件 | 说明 |
---|---|
net/netlink/genetlink.c | Generic Netlink协议框架实现文件 |
include/net/genetlink.h | 仅内核态可见的Generic Netlink协议头文件 |
include/linux/genetlink.h | 用户态也可见的Generic Netlink协议头文件 |
数据结构
genl_family
/**
* struct genl_family - generic netlink family
* @id: protocol family idenfitier
* @hdrsize: length of user specific header in bytes
* @name: name of family
* @version: protocol version
* @maxattr: maximum number of attributes supported
* @attrbuf: buffer to store parsed attributes
* @ops_list: list of all assigned operations
* @family_list: family list
* @mcast_groups: multicast groups list
*/
struct genl_family
{
unsigned int id;
unsigned int hdrsize;
char name[GENL_NAMSIZ]; // 每个family也拥有一个全局唯一的名字
unsigned int version;
unsigned int maxattr;
struct nlattr ** attrbuf; /* private */
struct list_head ops_list; /* private */ // 该family的命令操作集链表
struct list_head family_list; /* private */ // 将family对象保存在全局哈希表中
struct list_head mcast_groups; /* private */
};
- id
每个family拥有一个全局唯一的ID,该ID可以由faimily的实现指定(比较难保证唯一性),也可由内核自动分配(推荐做法),可用范围为(GENL_MIN_ID(0x10), GENL_MAX_ID(1023)];
- hdrsize
每个Generic Netlink消息首部除了标准的genlmsghdr外,family还可以追加自己的首部,只需要通过该字段指定自己追加的首部大小即可;
- maxattr/attrbuf
maxattr字段记录了该faimily支持的最大属性个数,如果指定了属性个数,Generic Netlink框架会负责分配attrbuf,该区域用于消息接收过程中的属性解析,具体见genl_rcv_msg()。
genl_ops
genl_ops定义一个family支持的命令。如genl_family的定义,每个family可以支持多个命令,这些命令被组织到genl_family.ops_list中。
/**
* struct genl_ops - generic netlink operations
* @cmd: command identifier
* @flags: flags
* @policy: attribute validation policy
* @doit: standard command callback
* @dumpit: callback for dumpers
* @done: completion callback for dumps
* @ops_list: operations list
*/
struct genl_ops
{
u8 cmd; // 命令ID,在family内部唯一标识命令
unsigned int flags;
const struct nla_policy *policy; // 属性策略,nlmsg_parse()会根据该策略对属性格式做基本的校验
int (*doit)(struct sk_buff *skb, struct genl_info *info);
int (*dumpit)(struct sk_buff *skb, struct netlink_callback *cb);
int (*done)(struct netlink_callback *cb);
struct list_head ops_list; // 将ops对象放入family->ops_list链表中
};
- flags
取值来自标准的Netlink消息中的NLM_F_XXX,不过Generic Netlink只用了其中部分标记,这些标记会影响消息的接收流程。
flags | 值 | 含义 |
---|---|---|
GENL_ADMIN_PERM | 0x1 | 表示该消息的发送需要管理员权限 |
GENL_CMD_CAP_DO | 0x2 | 表示该操作集指定了doit()回调 |
GENL_CMD_CAP_DUMP | 0x4 | 表示该操作集指定了dumpit()回调 |
GENL_CMD_CAP_HASPOL | 0x8 | 表示该操作集指定了属性策略 |
多播组: genl_multicast_group
/**
* struct genl_multicast_group - generic netlink multicast group
* @name: name of the multicast group, names are per-family
* @id: multicast group ID, assigned by the core, to use with
* genlmsg_multicast().
* @list: list entry for linking
* @family: pointer to family, need not be set before registering
*/
struct genl_multicast_group
{
struct genl_family *family; /* private */
struct list_head list; /* private */
char name[GENL_NAMSIZ]; // 多播组名字
u32 id; // 多播组ID,在整个Generic Netlink层面唯一
};
/*
* Bitmap of multicast groups that are currently in use.
*
* To avoid an allocation at boot of just one unsigned long,
* declare it global instead.
* Bit 0 is marked as already used since group 0 is invalid.
*/
static unsigned long mc_group_start = 0x1;
static unsigned long *mc_groups = &mc_group_start;
// 按位表示所有多播组需要的unsigned long整数个数
static unsigned long mc_groups_longs = 1;
Generic Netlink框架
+---------------------+ +---------------------+
| (3) application "A" | | (3) application "B" |
+------+--------------+ +--------------+------+
| |
\ /
\ /
| |
+-------+--------------------------------+-------+
| : : | user-space
=====+ : (5) kernel socket API : +================
| : : | kernel-space
+--------+-------------------------------+-------+
| |
+-----+-------------------------------+----+
| (1) Netlink subsystem |
+---------------------+--------------------+
|
+---------------------+--------------------+
| (2) Generic Netlink bus |
+--+--------------------------+-------+----+
| | |
+------