Linux Generic Netlink 设计与实现

本文深入解析GenericNetlink的内部结构,包括Family管理机制、自动分配ID的方法、Control Family的实现细节,以及用户空间如何查询Family列表。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

http://blog.chinaunix.net/uid-20273473-id-3137039.html

1. Generic Netlink 的数据结构
 
 
首先, Generic Netlink是按照Family管理的,内核的其它模块或子系统可以向它注册自己的Family. 所有的Family会存放在一张Hash链表上。
 
注册Family: 
  1. #define NLMSG_MIN_TYPE        0x10    /* < 0x10: reserved control messages */

  2. #define GENL_ID_GENERATE    0
  3. #define GENL_ID_CTRL        NLMSG_MIN_TYPE

  4. #define GENL_MIN_ID    NLMSG_MIN_TYPE
  5. #define GENL_MAX_ID    1023
如果Family ID是GENL_ID_GENERATE(被定义为0),则表示由系统自动分配ID,否则表示指定固定ID。 指定的ID必须在大于0x10并小于等于1023.   0x10被预留给CTRL_FAMILY.
自动分配ID的方法是从0x10开始找到第一个未被分配的ID。 可见Family ID是全局唯一的。
Family在Hash表上的位置由ID决定, ID & 0x0F, 也就是ID的低四Bit决定
Family name最好也是全局唯一的, 在genl_family_find_byname(char *name)函数中可见它是找到第一个匹配的name就返回的。
Family的maxattr:根据maxattr的值给struct nlattr ** attrbuf; 分配相应大小的内存。nlattr的作用后面会讲到。
 
每个Family都会有一个operations的列表,可以调用genl_register_ops()函数把各个ops添加到famliy 的ops_list;链表中。每个ops都有一个cmd和相应的Handler.
每个Family通常也都会有一个mc_group的列表。
每个mc_group都隶属于一个Famliy,  同一个Family内的mc_group不能有相同的name, 
Name是由用户指定的,但是ID是由系统分配的,在genl_register_mc_group()函数里面会分配ID
分配ID的方法是:在起始地址为mc_groups的内存地方设置一个bitmap, bit0初始化为1, 然后寻找第一个为零的Bit,该Bit的位置即为mc_group的ID, 并把该Bit置为1
这个Bitmap初始化时只有32Bit空间,随着Group数目的增多会动态增大。
可见:mc_group的ID是全局唯一的。
2. The Control Family
 
ctrl_family是一个特殊的Family, 它是由Generic Netlink自己注册和实现,并用来查询Family列表、管理各个Family的添加、删除等事件的。这里以ctrl_family为例看一下注册famly、注册ops、注册mcgroup的方法。

  1. static struct genl_family genl_ctrl = {
  2.     .id = GENL_ID_CTRL,
  3.     .name = "nlctrl",
  4.     .version = 0x2,
  5.     .maxattr = CTRL_ATTR_MAX,
  6.     .netnsok = true,
  7. };
ID是0x10, 名字是nlctrl
它只有一个ops:

  1. static struct genl_ops genl_ctrl_ops = {
  2.     .cmd        = CTRL_CMD_GETFAMILY,
  3.     .doit        = ctrl_getfamily,
  4.     .dumpit        = ctrl_dumpfamily,
  5.     .policy        = ctrl_policy,
  6. };
只有一个mc_group,  名字为notify:

  1. static struct genl_multicast_group notify_grp = {
  2.     .name        = "notify",
  3. };
这个group的ID被预留为0x10,而不是按照通用的规则分配的。
所谓policy, 是用来验证接收到的数据是否合适。 接收到数据由一个属性header(struct nlattr)和真正的数据构成, 对一种属性的数据, 定义一个policy,  如下代码表示如果数据CTRL_ATTR_FAMILY_ID属性的,那么其类型必须是U16.

  1. static const struct nla_policy ctrl_policy[CTRL_ATTR_MAX+1] = {
  2.     [CTRL_ATTR_FAMILY_ID]    = { .type = NLA_U16 },
  3.     [CTRL_ATTR_FAMILY_NAME]    = { .type = NLA_NUL_STRING,
  4.                  .len = GENL_NAMSIZ - 1 },
  5. };
3. 用户空间程序查询Family列表 

分析用户空间程序向ctrl_family发送CTRL_CMD_GETFAMILY命令后,内核的执行流程和向用户程序反送结果的流程。 libmnl-1.0.2里面的genl-family-get.c文件:

  1. nlh = mnl_nlmsg_put_header(buf);
  2.     nlh->nlmsg_type    = GENL_ID_CTRL;
  3.     nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
  4.     nlh->nlmsg_seq = seq = time(NULL);

  5.     genl = mnl_nlmsg_put_extra_header(nlh, sizeof(struct genlmsghdr));
  6.     genl->cmd = CTRL_CMD_GETFAMILY;
  7.     genl->version = 1;
可见做法是先构造nlmsghdr再构造genlmsghdr。
Nlmsghdr的nlmsg_type赋值为要接收消息的Family ID, 
Genlmsghdr的cmd 赋值为要发送的CMD

  1. mnl_attr_put_u32(nlh, CTRL_ATTR_FAMILY_ID, GENL_ID_CTRL);
  2.     if (argc >= 2)
  3.         mnl_attr_put_strz(nlh, CTRL_ATTR_FAMILY_NAME, argv[1]);
  4.     else
  5.         nlh->nlmsg_flags |= NLM_F_DUMP;
所谓attr, 意思就是标记要传送的数据是什么数据。
这里填充了两种属性的数据:CTRL_ATTR_FAMILY_ID和CTRL_ATTR_FAMILY_NAME
每种数据都是一个header紧跟真正的数据。如下图:
 
 
发送数据:

  1. if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
  2.         perror("mnl_socket_send");
  3.         exit(EXIT_FAILURE);
  4.     }
内核接收到消息后会交由genl_rcv_msg 函数处理, 该函数找到相应cmd对应的doit函数并执行之。对于CTRL_CMD_GETFAMILY来说,就是ctrl_dumpfamily函数。这个函数构造好回复信息后调用genlmsg_reply发送回复到用户程序。
用户空间程序怎样监听某一个mc_group: 用setsockopt
  1. mnl_socket_setsockopt(nl, NETLINK_ADD_MEMBERSHIP, &grp, sizeof(grp));
需要知道mc_group的ID才行。

<think>好的,用户在使用iw dev时遇到了“Failed to connect to generic netlink”的错误,需要我来帮忙解决。首先,我要回忆一下这个错误的常见原因。通常,这可能内核模块未加载、权限不足、或者iw版本不兼容有关。 用户之前询问过如何使用iw进行无线管理,现在遇到了具体的问题,可能是在按照之前的指导操作时出现的。这说明用户可能在尝试执行iw命令,但遇到了权限或配置问题。 接下来,我需要考虑可能的原因。首先,权限问题。用户是否使用了sudo?如果没有,可能权限不足。但用户可能已经用了sudo,所以需要进一步考虑其他原因。 然后是内核模块的问题。无线网卡的驱动模块是否加载?特别是cfg80211和mac80211模块,这些是支持通用netlink通信所必需的。如果模块未加载,iw就无法正常工作。 另外,iw版本过旧或者当前内核不兼容也可能导致这个问题。用户可能使用的是较旧的Linux发行版,或者自行编译的内核,导致兼容性问题。 还有可能是系统服务的问题,比如NetworkManager或其他网络服务iw冲突,或者干扰了netlink通信。 我需要逐步引导用户检查这些可能性。首先建议检查权限,确保使用sudo。然后检查内核模块是否加载,如果没有,尝试加载它们。接着查看模块是否存在,如果模块不存在,可能需要安装或更新驱动。然后检查iw和内核版本是否匹配,最后考虑是否有其他服务干扰。 在回答时,要分步骤说明,使用用户容易理解的命令,并解释每个步骤的目的。同时,要给出可能的解决方案,并建议用户如果问题依旧,提供更多系统信息以便进一步排查。 还要注意用户的可能背景,他们可能对Linux系统有一定了解,但遇到具体错误时需要明确的指导。避免使用过于专业的术语,但必要的术语需要解释清楚,比如内核模块、netlink等。 最后,确保回答结构清晰,步骤有序,并鼓励用户在问题未解决时提供更多细节,以便进一步帮助。</think>当你运行 `iw dev` 命令时出现 **`Failed to connect to generic netlink`** 错误,通常是因为系统无法通过 Netlink 协议内核的无线子系统通信。以下是常见原因及解决方法: --- ### **原因 1:权限不足** `iw` 需要 **root 权限** 才能访问内核的无线配置接口。 - **解决方法**: ```bash sudo iw dev # 使用 sudo 提权 ``` --- ### **原因 2:无线内核模块未加载** Linux 的无线功能依赖内核模块(如 `cfg80211` 和 `mac80211`),如果未加载或驱动异常,会导致通信失败。 - **解决方法**: 1. **检查模块是否加载**: ```bash lsmod | grep cfg80211 # 确认 cfg80211/mac80211 是否存在 ``` 若无输出,需手动加载模块: ```bash sudo modprobe cfg80211 # 加载核心无线模块 sudo modprobe mac80211 # 加载无线协议栈 ``` 2. **检查无线网卡驱动**: ```bash lspci -k | grep -iA3 network # PCI 无线网卡 lsusb | grep -i wireless # USB 无线网卡 ``` 确保网卡驱动已正确加载(如 `iwlwifi` 用于 Intel 网卡)。 --- ### **原因 3:内核模块缺失或损坏** 如果系统缺少无线相关模块(如裁剪过的内核或自定义编译内核),会导致功能异常。 - **解决方法**: - 重新安装内核模块包(不同发行版包名不同): ```bash # Debian/Ubuntu sudo apt install --reinstall linux-modules-extra-$(uname -r) # Arch sudo pacman -S linux-firmware ``` --- ### **原因 4:iw 版本内核不兼容** 旧版 `iw` 可能不支持新内核的接口。 - **解决方法**: 更新 `iw` 到最新版本: ```bash # Debian/Ubuntu sudo apt update && sudo apt upgrade iw # CentOS/RHEL sudo yum update iw # Arch sudo pacman -Syu iw ``` --- ### **原因 5:其他服务占用 Netlink 接口** 某些网络管理工具(如 NetworkManager)可能冲突或导致资源锁定。 - **解决方法**: 1. 临时停止 NetworkManager: ```bash sudo systemctl stop NetworkManager ``` 2. 重新运行 `iw dev`,若成功,说明冲突存在,需调整服务配置。 --- ### **操作流程总结** 1. 尝试 `sudo iw dev`。 2. 检查并加载内核模块 `cfg80211` 和 `mac80211`。 3. 更新 `iw` 和内核模块。 4. 排查驱动和硬件兼容性。 --- ### **附:快速诊断命令** ```bash # 查看内核日志中的无线相关错误 dmesg | grep -iE 'cfg80211|mac80211|wlan' # 检查无线接口是否被识别 ip link show # 查找 wlan0/wlp3s0 等接口 ``` 如果问题仍未解决,请提供以下信息: - 操作系统及版本(如 `lsb_release -a`) - 无线网卡型号(如 `lspci -nnk`) - 完整错误日志(如 `sudo iw dev 2>&1`)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值