本文分析了5.15内核的genl代码。
genl的内核文件主要有:
include/net/genetlink.h
net/netlink/genetlink.c
首先得定义一个struct genl_family类型的全局变量。
然后调用 int genl_register_family(struct genl_family *family) 注册。
调用 int genl_unregister_family(const struct genl_family *family) 注销。
看看genl_family结构体的定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | struct genl_family { int id; /* private */ unsigned int hdrsize; char name[GENL_NAMSIZ]; unsigned int version; unsigned int maxattr; unsigned int mcgrp_offset; /* private */ u8 netnsok:1; u8 parallel_ops:1; u8 n_ops; u8 n_small_ops; u8 n_mcgrps; const struct nla_policy *policy; int (*pre_doit)( const struct genl_ops *ops, struct sk_buff *skb, struct genl_info *info); void (*post_doit)( const struct genl_ops *ops, struct sk_buff *skb, struct genl_info *info); const struct genl_ops * ops; const struct genl_small_ops *small_ops; const struct genl_multicast_group *mcgrps; struct module *module; }; |
id | 每个genl family都有一个id,这个id是注册的时候由内核分配的。注册的时候提供的是name,用户态和内核态都是通过id通信的。用户态通过一个特殊的ctrl family可以通过name获得一个genl family的信息。 |
hdrsize | 通信头的大小,内核解析attributes时,会跳过这个大小的字节数。 |
name | genl family的名字,由注册者提供一个唯一的名字。 |
version | genl family的版本。 |
maxattr policy | 用来解析attr,在policy小节分析 |
mcgrp_offset n_mcgrps mcgrps | 组播相关,在第组播小节分析 |
netnsok | 是否支持多个net namespace,不分析,如果没有需求,为0即可。 |
parallel_ops | 是否支持并行,不支持的话,在内核处理消息的时候,会上一个全局锁genl_unlock() |
pre_doit post_doit | 在调用ops的doit之前,调用这个pre_doit 在调用ops的doit之后,调用这个post_doit 通常为NULL,根据业务需要设置。 |
n_ops ops | 定义ops |
n_small_ops small_ops | 定义small ops。small ops结构体更小,去掉了一些不常用的功能,更省内存。 |
看一个net/wireless/nl80211.c的一个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | static struct genl_family nl80211_fam __ro_after_init = { .name = NL80211_GENL_NAME, /* have users key off the name instead */ .hdrsize = 0, /* no private header */ .version = 1, /* no particular meaning now */ .maxattr = NL80211_ATTR_MAX, .policy = nl80211_policy, .netnsok = true , .pre_doit = nl80211_pre_doit, .post_doit = nl80211_post_doit, .module = THIS_MODULE, .ops = nl80211_ops, .n_ops = ARRAY_SIZE(nl80211_ops), .small_ops = nl80211_small_ops, .n_small_ops = ARRAY_SIZE(nl80211_small_ops), .mcgrps = nl80211_mcgrps, .n_mcgrps = ARRAY_SIZE(nl80211_mcgrps), .parallel_ops = true , }; int __init nl80211_init( void ) { int err; err = genl_register_family(&nl80211_fam); if (err) return err; err = netlink_register_notifier(&nl80211_netlink_notifier); if (err) goto err_out; return 0; err_out: genl_unregister_family(&nl80211_fam); return err; } void nl80211_exit( void ) { netlink_unregister_notifier(&nl80211_netlink_notifier); genl_unregister_family(&nl80211_fam); } |
每一个genl family通常都有很多的命令。用户态通过某一个命令和内核交互,一个命令就是一个ops。
ops有2种:genl_ops和genl_small_ops。后者的成员少很多:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | struct genl_ops { int (*doit)( struct sk_buff *skb, struct genl_info *info); int (*start)( struct netlink_callback *cb); int (*dumpit)( struct sk_buff *skb, struct netlink_callback *cb); int (*done)( struct netlink_callback *cb); const struct nla_policy *policy; unsigned int maxattr; u8 cmd; u8 internal_flags; u8 flags; u8 validate; }; struct genl_small_ops { int (*doit)( struct sk_buff *skb, struct genl_info *info); int (*dumpit)( struct sk_buff *skb, struct netlink_callback *cb); u8 cmd; u8 internal_flags; u8 flags; u8 validate; }; |
doit | 对接用户态的非dump命令 |
dumpit | 对接用户态的dump请求,传入的netlink msg中的flags需要置位NLM_F_DUMP |
cmd | cmd是命令的id。命令的id没有特殊的要求,只要是u8就可以。 ops数组中,cmd可以不连续。因为内核会遍历ops数组去匹配cmd。 内核会先检查ops,在检查small ops。直到找到匹配的ops。 |
policy maxattr validate | 见policy小节。 |
flags | 主要指定这个ops需要的权限等,如指定GENL_ADMIN_PERM |
internal_flags | 当前没看到用的地方 |
start | 用在dump,先执行下start |
done | 用在dump,结束后执行下done |
policy是一个struct nla_policy数组,nla_policy定义了一个attribute的类型、验证类型、长度等。
nla_policy是netlink层定义的功能。头文件:include/net/netlink.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | struct nla_policy { u8 type; u8 validation_type; u16 len; union { const u32 bitfield32_valid; const u32 mask; const char *reject_message; const struct nla_policy *nested_policy; struct netlink_range_validation *range; struct netlink_range_validation_signed *range_signed; struct { s16 min, max; }; int (*validate)( const struct nlattr *attr, struct netlink_ext_ack *extack); u16 strict_start_type; }; }; |
type
attribute的类型,比如NLA_U16,
validation_type
验证类型,有下述定义:
enum nla_policy_validation {
NLA_VALIDATE_NONE,
NLA_VALIDATE_RANGE,
NLA_VALIDATE_RANGE_WARN_TOO_LONG,
NLA_VALIDATE_MIN,
NLA_VALIDATE_MAX,
NLA_VALIDATE_MASK,
NLA_VALIDATE_RANGE_PTR,
NLA_VALIDATE_FUNCTION,
};
len
用来指定attribute的长度,根据type不同,有不同的含义,netlink.h有详细的注释。
union的字段貌似不需要手动定义,而通过netlink通过的宏来定义一个policy,如:
#define NLA_POLICY_RANGE(tp, _min, _max) { \
.type = NLA_ENSURE_INT_OR_BINARY_TYPE(tp), \
.validation_type = NLA_VALIDATE_RANGE, \
.min = _min, \
.max = _max \
}