我尝试使用netlink套接字和通用消息类型实现内核用户通信。到目前为止,我能够将消息从用户空间发送到内核,然后将消息发送回用户空间。问题是,在我的用户空间程序中,我总是收到一条错误消息,表明收到了无效/格式错误的消息。在用户空间程序中,我使用libnl进行netlink通信。
相关的netlink内核代码如下所示:
enum nl_tdisk_attr {
NL_UNSPEC,
NL_MY_ATTR, //My argument
__NL_MAX
};
#define NL_MAX (__NL_MAX - 1)
enum nl_tdisk_msg_types {
NL_CMD_READ = 0,
NL_CMD_MY_CMD //My command
NL_CMD_MAX
};
//Family definition
static struct genl_family family = {
.id = GENL_ID_GENERATE,
.name = "my-family",
.hdrsize = 0,
.version = 0,
.maxattr = NL_MAX,
};
//Command definition
static struct genl_ops ops[] = {
{
.cmd = NL_CMD_MY_CMD,
.doit = genl_register,
}
};
//...
//When the module is loaded:
genl_register_family_with_ops(&family, ops);
//Now some data should be sent to user space:
struct sk_buff *msg= nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
void *hdr = genlmsg_put(msg, port/*note1*/, 0, &family, 0/*note2*/, NL_CMD_MY_CMD);
nla_put_u32(msg, NL_MY_ATTR, some_value);
genlmsg_end(msg, hdr);
genlmsg_unicast(&init_net, msg, port/*note1*/); //note3
请注意,我删除了错误检查以减少代码量
一些注意事项:
genlmsg_unicast
始终返回0,表示邮件已成功发送。所以我假设内核代码应该没问题。这里是用户空间代码:
#include <netlink/netlink.h>
#include <netlink/socket.h>
#include <netlink/types.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/ctrl.h>
#include <netlink/genl/mngt.h>
//...
struct nl_sock *socket = nl_socket_alloc();
//I explicitly set those callbacks to get some debug information
nl_socket_modify_cb(socket, NL_CB_MSG_IN, NL_CB_DEBUG, NULL, NULL);
nl_socket_modify_cb(socket, NL_CB_INVALID, NL_CB_DEBUG, NULL, NULL);
//I also tried to Play around with the buffer size:
nl_socket_set_buffer_size(socket, 65536, 65536);
genl_connect(socket);
familyId = genl_ctrl_resolve(socket, "my-family"); //This works and gives me the correct Family id
nl_recvmsgs_default(socket);
一旦内核发送消息,我就会在用户空间程序中看到调试信息,但遗憾的是它只是错误消息:
-- Debug: Received Message:
-------------------------- BEGIN NETLINK MESSAGE ---------------------------
[NETLINK HEADER] 16 octets
.nlmsg_len = 308
.type = 23 <0x17>
.flags = 0
.seq = 0
.port = -1765782228
[GENERIC NETLINK HEADER] 4 octets
.cmd = 1
.version = 1
.unused = 0
[PAYLOAD] 4 octets
08 00 02 00 ....
--------------------------- END NETLINK MESSAGE ---------------------------
-- Error: Invalid message: type=0x17 length=24 flags=0 sequence-nr=0 pid=2529185068
正如您所看到的那样,在行&#34; END NETLINK MESSAGE&#34;这是来自回调NL_CB_INVALID
的消息,它告诉我收到了无效消息。
所以实际上,沟通本身是有效的,因为它只是收到无效的消息,不知道为什么。有谁知道我可以在哪里寻找更多信息?为什么信息格式错误...... 甚至更好:有没有人在我的代码中看到错误? 或者有没有人知道一个非常好的网站,它描述了这样一个场景?
答案 0 :(得分:2)
经过长时间的试验和错误后,我终于找到了某种的解决方案。问题实际上是修改“无效消息”回调:nl_socket_modify_cb(socket, NL_CB_INVALID, NL_CB_DEBUG, NULL, NULL);
通过修改它,nl_recvmsgs_default(socket);
总是返回0表示没有错误。删除该回调后,我意识到nl_recvmsgs_default(socket);
返回了-16
,根据文档说明,它表示“消息序列号不匹配”。由于某种原因,它不接受序号0,我不知道为什么......
要解决问题,我在用户空间程序中添加了nl_socket_disable_seq_check(socket);
。我想这不是最佳解决方案,所以如果你知道更好的解决方案,请告诉我!
答案 1 :(得分:2)
(顺便说一句:如果你没有先阅读@ ThomasSparber自己的答案,这个答案是没有意义的,它确定了问题的根源和解决方法。)
您可以在genlmsg_put
期间指定序列号。 libnl期望响应seqnum与请求的相同。
假设您在genlmsg_put
期间致电genl_register
:
int genl_register(struct sk_buff *skb, struct genl_info *info)
{
...
genlmsg_put(msg, port, info->nlhdr->nlmsg_seq, &family, 0,
NL_CMD_MY_CMD);
...
}
应该这样做。禁用seqnum分析可能不好,因为您可能会在多线程用户空间客户端和诸如此类的东西中混合请求响应。
顺便说一下,这也可能不好:
struct sk_buff *msg= nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
NLMSG_GOODSIZE
对于nlmsg_new
来说不是一个好的尺寸;它整个包的尺寸很好。整个数据包是您发送到nlmsg_new
加at least the netlink header size的任何内容,并且您不希望它超过PAGE_SIZE
。 NLMSG_DEFAULT_SIZE
通常是nlmsg_new
的更好候选人。
但是,因为你正在使用 Generic Netlink,你可能想要完全划掉它并做
struct sk_buff *msg= genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
(不幸的是,GENLMSG_DEFAULT_SIZE
在较旧的内核中不可用。)