Я хотел бы начать, сказав, что я не большой поклонник Netlink; Я думаю, что это довольно плохо спроектировано. Тем не менее, я думаю, что у меня есть точный ответ на вопрос, так что вот оно.
Основная проблема заключается в том, что перед использованием общего семейства Netlink вам сначала необходимо зарегистрировать его (это также относится к обычным семействам Netlink). Ядро не может обрабатывать семьи, которых он не знает. Если вы не используете уже существующее семейство, это формирует то, как вы должны приближаться к Netlink.
A Generic Netlink Family belongs to a kernel module. Это означает, что клиент пользовательского пространства не может создать семью. В свою очередь, это означает, что вы не можете просто запустить клиент, а затем модуль отправит сообщение сразу после его создания. Это происходит потому, что семья не существует в тот момент, когда клиент хотел связать себя с ней.
Что вам нужно сделать, это:
- Создать и зарегистрировать семью и группу многоадресной передачи, а вы вставляя модуль.
- Запустите клиент пользовательского пространства и привяжите его к семейству и многоадресной группе.
- Имейте модуль ядра отправить сообщение в какой-то момент (после связывания клиента).
- Клиент пользовательского пространства теперь получает сообщение.
- Когда модуль удален, он должен отменить регистрацию семьи.
Ниже приведена моя версия вашего кода. Это модуль ядра. Как вы можете видеть, я решил отправить сообщение несколько раз по таймеру, который запускается каждые две секунды. Это дает вам время, чтобы запустить клиент:
#include <linux/kernel.h>
#include <linux/module.h>
#include <net/genetlink.h>
static struct timer_list timer;
/**
* This callback runs whenever the socket receives messages.
* We don't use it now, but Linux complains if we don't define it.
*/
static int hello(struct sk_buff *skb, struct genl_info *info)
{
pr_info("Received a message in kernelspace.\n");
return 0;
}
/**
* Attributes are fields of data your messages will contain.
* The designers of Netlink really want you to use these instead of just dumping
* data to the packet payload... and I have really mixed feelings about it.
*/
enum attributes {
/*
* The first one has to be a throwaway empty attribute; I don't know
* why.
* If you remove it, ATTR_HELLO (the first one) stops working, because
* it then becomes the throwaway.
*/
ATTR_DUMMY,
ATTR_HELLO,
ATTR_FOO,
/* This must be last! */
__ATTR_MAX,
};
/**
* Here you can define some constraints for the attributes so Linux will
* validate them for you.
*/
static struct nla_policy policies[] = {
[ATTR_HELLO] = { .type = NLA_STRING, },
[ATTR_FOO] = { .type = NLA_U32, },
};
/**
* Message type codes. All you need is a hello sorta function, so that's what
* I'm defining.
*/
enum commands {
COMMAND_HELLO,
/* This must be last! */
__COMMAND_MAX,
};
/**
* Actual message type definition.
*/
struct genl_ops ops[] = {
{
.cmd = COMMAND_HELLO,
.flags = 0,
.policy = policies,
.doit = hello,
.dumpit = NULL,
},
};
/**
* A Generic Netlink family is a group of listeners who can and want to speak
* your language.
* Anyone who wants to hear your messages needs to register to the same family
* as you.
*/
struct genl_family family = {
.id = GENL_ID_GENERATE,
.hdrsize = 0,
.name = "PotatoFamily",
.version = 1,
.maxattr = __ATTR_MAX,
};
/**
* And more specifically, anyone who wants to hear messages you throw at
* specific multicast groups need to register themselves to the same multicast
* group, too.
*/
struct genl_multicast_group groups[] = {
{ .name = "PotatoGroup" },
};
void send_multicast(unsigned long arg)
{
struct sk_buff *skb;
void *msg_head;
unsigned char *msg = "TEST";
int error;
pr_info("----- Running timer -----\n");
pr_info("Newing message.\n");
skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
if (!skb) {
pr_err("genlmsg_new() failed.\n");
goto end;
}
pr_info("Putting message.\n");
msg_head = genlmsg_put(skb, 0, 0, &family, 0, COMMAND_HELLO);
if (!msg_head) {
pr_err("genlmsg_put() failed.\n");
kfree_skb(skb);
goto end;
}
pr_info("Nla_putting string.\n");
error = nla_put_string(skb, ATTR_HELLO, msg);
if (error) {
pr_err("nla_put_string() failed: %d\n", error);
kfree_skb(skb);
goto end;
}
pr_info("Nla_putting integer.\n");
error = nla_put_u32(skb, ATTR_FOO, 12345);
if (error) {
pr_err("nla_put_u32() failed: %d\n", error);
kfree_skb(skb);
goto end;
}
pr_info("Ending message.\n");
genlmsg_end(skb, msg_head);
pr_info("Multicasting message.\n");
/*
* The family has only one group, so the group ID is just the family's
* group offset.
* mcgrp_offset is supposed to be private, so use this value for debug
* purposes only.
*/
pr_info("The group ID is %u.\n", family.mcgrp_offset);
error = genlmsg_multicast_allns(&family, skb, 0, 0, GFP_KERNEL);
if (error) {
pr_err("genlmsg_multicast_allns() failed: %d\n", error);
pr_err("(This can happen if nobody is listening. "
"Because it's not that unexpected, "
"you might want to just ignore this error.)\n");
goto end;
}
pr_info("Success.\n");
end:
mod_timer(&timer, jiffies + msecs_to_jiffies(2000));
}
static int init_socket(void)
{
int error;
pr_info("Registering family.\n");
error = genl_register_family_with_ops_groups(&family, ops, groups);
if (error)
pr_err("Family registration failed: %d\n", error);
return error;
}
static void initialize_timer(void)
{
pr_info("Starting timer.\n");
init_timer(&timer);
timer.function = send_multicast;
timer.expires = 0;
timer.data = 0;
mod_timer(&timer, jiffies + msecs_to_jiffies(2000));
}
static int __init hello_init(void)
{
int error;
error = init_socket();
if (error)
return error;
initialize_timer();
pr_info("Hello module registered.\n");
return 0;
}
static void __exit hello_exit(void)
{
del_timer_sync(&timer);
genl_unregister_family(&family);
pr_info("Hello removed.\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
И это клиент в пользовательском пространстве:
#include <netlink/netlink.h>
#include <netlink/socket.h>
#include <netlink/msg.h>
#include <netlink/genl/genl.h>
static struct nl_sock *sk = NULL;
/**
* Attributes and commands have to be the same as in kernelspace, so you might
* want to move these enums to a .h and just #include that from both files.
*/
enum attributes {
ATTR_DUMMY,
ATTR_HELLO,
ATTR_FOO,
/* This must be last! */
__ATTR_MAX,
};
enum commands {
COMMAND_HELLO,
/* This must be last! */
__COMMAND_MAX,
};
static int fail(int error, char *func_name)
{
printf("%s() failed.\n", func_name);
return error;
}
static int nl_fail(int error, char *func_name)
{
printf("%s (%d)\n", nl_geterror(error), error);
return fail(error, func_name);
}
/*
* This function will be called for each valid netlink message received
* in nl_recvmsgs_default()
*/
static int cb(struct nl_msg *msg, void *arg)
{
struct nlmsghdr *nl_hdr;
struct genlmsghdr *genl_hdr;
struct nlattr *attrs[__ATTR_MAX];
int error;
printf("The kernel module sent a message.\n");
nl_hdr = nlmsg_hdr(msg);
genl_hdr = genlmsg_hdr(nl_hdr);
if (genl_hdr->cmd != COMMAND_HELLO) {
printf("Oops? The message type is not Hello; ignoring.\n");
return 0;
}
error = genlmsg_parse(nl_hdr, 0, attrs, __ATTR_MAX - 1, NULL);
if (error)
return nl_fail(error, "genlmsg_parse");
/* Remember: attrs[0] is a throwaway. */
if (attrs[1])
printf("ATTR_HELLO: len:%u type:%u data:%s\n",
attrs[1]->nla_len,
attrs[1]->nla_type,
(char *)nla_data(attrs[1]));
else
printf("ATTR_HELLO: null\n");
if (attrs[2])
printf("ATTR_FOO: len:%u type:%u data:%u\n",
attrs[2]->nla_len,
attrs[2]->nla_type,
*((__u32 *)nla_data(attrs[2])));
else
printf("ATTR_FOO: null\n");
return 0;
}
static int do_things(void)
{
struct genl_family *family;
int group;
int error;
/* Socket allocation yadda yadda. */
sk = nl_socket_alloc();
if (!sk)
return fail(-1, "nl_socket_alloc");
nl_socket_disable_seq_check(sk);
error = nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM, cb, NULL);
if (error)
return nl_fail(error, "nl_socket_modify_cb");
error = genl_connect(sk);
if (error)
return nl_fail(error, "genl_connect");
/* Find the multicast group identifier and register ourselves to it. */
group = genl_ctrl_resolve_grp(sk, "PotatoFamily", "PotatoGroup");
if (group < 0)
return nl_fail(group, "genl_ctrl_resolve_grp");
printf("The group is %u.\n", group);
error = nl_socket_add_memberships(sk, group, 0);
if (error) {
printf("nl_socket_add_memberships() failed: %d\n", error);
return error;
}
/* Finally, receive the message. */
nl_recvmsgs_default(sk);
return 0;
}
int main(void)
{
int error;
error = do_things();
if (sk)
nl_socket_free(sk);
return error;
}
Вы должны использовать целое число, а не структура genl_multicast_group в этом вызове. Посмотрите, как это делается в другом месте ядра. Также обратите внимание, что это целое число имеет верхний предел, что-то вроде 32. –
Спасибо за ваш комментарий. И да, верхний предел равен 32. Я попытаюсь скомпилировать исправленную версию. – user4122787
Итак, компиляция модуля ядра прошла успешно (спасибо и извините, что я не могу выдвинуть ваш комментарий ...). Но я до сих пор не могу получать сообщения в Userland. (и точно так же, как и информация: он скомпилировался в последний раз, «ошибка» выше была просто предупреждением и как это не является источником проблемы.) – user4122787