我可以在内核空间中拥有超过 32 个 netlink 套接字吗?

Can I have more than 32 netlink sockets in kernelspace?

我有几个需要与用户空间交互的内核模块。因此,每个模块都有一个 Netlink 套接字。

我的问题是这些套接字相互干扰。这是因为它们都注册到同一个 Netlink 地址系列(因为没有多少可用的开头 - max is 32 and more than half are already reserved),也因为它们都将自己绑定到同一个 pid(内核 pid - 零) .

我希望地址族有更多空间。或者,更好的是,我希望我可以将我的套接字绑定到其他 pids。如果一次只能打开 32 个套接字,为什么 Netlink 是首选的用户内核通道?

libnl-3's documentation

The netlink address (port) consists of a 32bit integer. Port 0 (zero) is reserved for the kernel and refers to the kernel side socket of each netlink protocol family. Other port numbers usually refer to user space owned sockets, although this is not enforced.

最后一个说法现在看来是个谎言。内核使用 constant as pid 并且不导出更通用的函数:

if (netlink_insert(sk, 0))
    goto out_sock_release;

我想我可以重新编译内核并增加地址族限制。但这些是内核模块;我不应该那样做。

我是不是漏掉了什么?

你被MAX_LINKS变量名弄糊涂了。它不是 "maxumum amount of links",而是 "maximum amount of families"。您列出的内容是 netlink families 或 IOW netlink groups。确实有32家庭。每个家庭都致力于为某个特定目的服务。例如 NETLINK_SELINUX 用于 SELinux 通知,NETLINK_KOBJECT_UEVENT 用于 kobject 通知(这些是 udev 处理的内容)。

但是每个家庭的插座数量没有限制。

当您调用 netlink_create 时,它会检查您的协议号,如果是 netlink 套接字,则它是 netlink 系列,如 NETLINK_SELINUX。看看the code

static int netlink_create(struct net *net, struct socket *sock, int protocol,
                           int kern)
{
...
         if (protocol < 0 || protocol >= MAX_LINKS)
                 return -EPROTONOSUPPORT;
...

这就是您的 MAX_LINKS 使用的方式。

稍后,当实际创建套接字时它会调用 __netlink_create, which in turn calls sk_alloc, which in turn calls sk_prot_alloc。现在,在 sk_prot_alloc 中,它通过 kmallocing 分配套接字(netlink 没有自己的 slab 缓存):

slab = prot->slab;
if (slab != NULL) {
    sk = kmem_cache_alloc(slab, priority & ~__GFP_ZERO);
    if (!sk)
        return sk;
    if (priority & __GFP_ZERO) {
        if (prot->clear_sk)
            prot->clear_sk(sk, prot->obj_size);
        else
            sk_prot_clear_nulls(sk, prot->obj_size);
    }
} else
    sk = kmalloc(prot->obj_size, priority);

没有

Netlink 的套接字数限制是 Generic Netlink 存在的原因。

通用 Netlink 是普通 Netlink 之上的一层。您不是打开一个套接字,而是在一个已经建立的套接字上注册一个回调,并在那里收听定向到 "sub"-family 的消息。鉴于有更多可用的系列插槽 (1023) 并且没有端口,我假设他们认为在这一层不需要将系列和端口分开。

要在内核空间中注册一个监听器,使用genl_register_family() or its siblings. In userspace, Generic Netlink can be used via libnl-3's API(虽然它相当有限,但代码说明了很多并且是开放的)。