linux / Generic Netlink

一、概述

Generic Netlink 是内核专门为了扩展 netlink 协议簇而设计的“通用netlink协议簇”。由于 netlink 协议最多支持 32 个协议簇,目前 Linux4.1 的内核中已经使用其中 21 个,对于用户需要定制特殊的协议类型略显不够,而且用户还需自行在 include/linux/netlink.h 中添加簇定义,但有时不方便,为此 Linux 设计了这种通用 Netlink 协议簇,用户可在此之上定义更多类型的子协议。

Generic Netlink 使用 NETLINK_GENERIC 类型协议簇,同样基于 netlink 子系统。具体框架如下:         

Genetlink 消息基于这个消息结构类型并定制化为如下结构:

其中 family 头对于 Genetlink 来说就是 Generic 消息头 genlmsghdr,接下来是可选的用户特定消息头,最后才是可选的有效载荷,即一个个消息属性实例。Genetlink 消息是命令驱动式的,即每一条消息的 genlmsghdr 中都指明了当前消息的 cmd 消息命令,这些消息 cmd 命令由用户自行定义。内核在接收到用户的 genl 消息后,首先会对命令 cmd 做判断,找到对应的消息处理结构(可能会执行 attr 有效性检查),然后才会去调用消息处理回调函数从消息载荷区中读取并处理其所需要的的 attr 属性载荷。

二、主要数据

2.1、struct nlmsghdr

struct nlmsghdr {
	__u32		nlmsg_len;	/* Length of message including header */
	__u16		nlmsg_type;	/* Message content */
	__u16		nlmsg_flags;	/* Additional flags */
	__u32		nlmsg_seq;	/* Sequence number */
	__u32		nlmsg_pid;	/* Sending process port ID */
};

struct nlmsghdr 为 netlink socket 自己的消息头,netlink 的内核实现将利用这个消息头来多路复用和多路分解以及其它的一些控制,因此它也被称为netlink 控制块。因此,应用在发送 netlink 消息时必须提供该消息头。

(1)字段 nlmsg_len 指定消息的总长度,包括紧跟该结构的数据部分长度以及该结构的大小;

(2)字段 nlmsg_type 用于应用内部定义消息的类型,它对 netlink 内核实现是透明的,因此大部分情况下设置为 0;

字段 nlmsg_flags 用于设置消息标志,可用的标志包括:

/* Flags values */
#define NLM_F_REQUEST           1       /* It is request message.       */
#define NLM_F_MULTI             2       /* Multipart message, terminated by NLMSG_DONE */
#define NLM_F_ACK               4       /* Reply with ack, with zero or error code */
#define NLM_F_ECHO              8       /* Echo this request            */
/* Modifiers to GET request */
#define NLM_F_ROOT      0x100   /* specify tree root    */
#define NLM_F_MATCH     0x200   /* return all matching  */
#define NLM_F_ATOMIC    0x400   /* atomic GET           */
#define NLM_F_DUMP      (NLM_F_ROOT|NLM_F_MATCH)
/* Modifiers to NEW request */
#define NLM_F_REPLACE   0x100   /* Override existing            */
#define NLM_F_EXCL      0x200   /* Do not touch, if it exists   */
#define NLM_F_CREATE    0x400   /* Create, if it does not exist */
#define NLM_F_APPEND    0x800   /* Add to end of list           */
  • 标志 NLM_F_REQUEST 用于表示消息是一个请求,所有应用首先发起的消息都应设置该标志。
  • 标志 NLM_F_MULTI 用于指示该消息是一个多部分消息的一部分,后续的消息可以通过宏NLMSG_NEXT来获得。
  • 宏 NLM_F_ACK 表示该消息是前一个请求消息的响应,顺序号与进程 ID 可以把请求与响应关联起来。
  • 标志 NLM_F_ECHO 表示该消息是相关的一个包的回传。
  • 标志 NLM_F_ROOT 被许多 netlink 协议的各种数据获取操作使用,该标志指示被请求的数据表应当整体返回用户应用,而不是一个条目一个条目地返回。有该标志的请求通常导致响应消息设置 NLM_F_MULTI 标志。注意,当设置了该标志时,请求是协议特定的,因此,需要在字段 nlmsg_type 中指定协议类型。
  • 标志 NLM_F_MATCH 表示该协议特定的请求只需要一个数据子集,数据子集由指定的协议特定的过滤器来匹配。
  • 标志 NLM_F_ATOMIC 指示请求返回的数据应当原子地收集,这预防数据在获取期间被修改。
  • 标志 NLM_F_DUMP 未实现。
  • 标志 NLM_F_REPLACE 用于取代在数据表中的现有条目。
  • 标志 NLM_F_EXCL_ 用于和 CREATE 和 APPEND 配合使用,如果条目已经存在,将失败。
  • 标志 NLM_F_CREATE 指示应当在指定的表中创建一个条目。
  • 标志 NLM_F_APPEND 指示在表末尾添加新的条目。

(3)内核需要读取和修改这些标志,对于一般的使用,用户把它设置为 0 就可以,只是一些高级应用(如 netfilter 和路由 daemon 需要它进行一些复杂的操作),字段 nlmsg_seq 和 nlmsg_pid 用于应用追踪消息,前者表示顺序号,后者为消息来源进程 ID。下面是一个示例:

#define MAX_MSGSIZE 1024
char buffer[] = "An example message";
struct nlmsghdr nlhdr;
nlhdr = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_MSGSIZE));
strcpy(NLMSG_DATA(nlhdr),buffer);
nlhdr->nlmsg_len = NLMSG_LENGTH(strlen(buffer));
nlhdr->nlmsg_pid = getpid();  /* self pid */
nlhdr->nlmsg_flags = 0;

结构 struct iovec 用于把多个消息通过一次系统调用来发送,下面是该结构使用示例:

struct iovec iov;
iov.iov_base = (void *)nlhdr;
iov.iov_len = nlh->nlmsg_len;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;

在完成以上步骤后,消息就可以通过下面语句直接发送:

sendmsg(fd, &msg, 0);

2.2、struct genlmsghdr

struct genlmsghdr {
    __u8    cmd;
    __u8    version;
    __u16    reserved;
};

Generic Netlink 消息头结构。Generic Netlink 消息头比较简单,仅包含了两个字段。

cmd 表示消息命令,对于用户自己定义的每个子协议类型都需要定义特定的消息命令集,这里该字段表示当前消息的消息命令;

version 字段表示版本控制,可以在在不破坏向后兼容性的情况下修改消息的格式,可以不使用该字段;

reserved 字段保留。

2.3、struct nlattr

从 nlattr 类型定义来看,是非常简单,但是 OVS 使用该数据结构来定义 action,nlattr 自身是不存储数据,而使用来定义数据格式的,OVS 也会使用 skb 来存储真实数据。

nlattr 数据结构定义

struct nlattr {
    uint16_t nla_len;     //数据长度
    uint16_t nla_type;    //数据类型
};

使用 nlattr 构造的数据结构如下图所示:

相关函数的定义如下:

nla_len 函数

static inline int nla_len(const struct nlattr *nla)   //返回数据的长度
{
	return nla->nla_len - NLA_HDRLEN;     //数据总长度减去头长度
}

nla_data 函数

static inline void *nla_data(const struct nlattr *nla)   //返回数据的首地址
{
	return (char *) nla + NLA_HDRLEN;   //当前指针加上头长度
}

nla_next 函数

//返回下一个数据的首地址
static inline struct nlattr *nla_next(const struct nlattr *nla, int *remaining)    
{
	int totlen = NLA_ALIGN(nla->nla_len);
    // //当remaining为0时,说明数据已经取完,返回值不能使用
	*remaining -= totlen;<span style="white-space:pre">					</span>
	return (struct nlattr *) ((char *) nla + totlen);
}

(SAW:Game Over!)

<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`)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值