ILD

generic netlink kernel module development
作者:Yuan Jianpeng 邮箱:yuanjp89@163.com
发布时间:2025-2-5 站点:Inside Linux Development

本文分析了5.15内核的genl代码。


genl的内核文件主要有:

include/net/genetlink.h

net/netlink/genetlink.c


1 注册genl family

首先得定义一个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时,会跳过这个大小的字节数。
namegenl family的名字,由注册者提供一个唯一的名字。
versiongenl 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);
}


2 ops

每一个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


doit处理流程


dumpit处理流程


3 policy

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                                     \

}


4 multicast





Copyright © linuxdev.cc 2017-2024. Some Rights Reserved.