私は Netlink の大ファンではないということから始めたいと思います。かなり設計が悪いと思います。そうは言っても、私は質問に対する正確な答えを持っていると思うので、ここに行きます。
あなたの中心的な問題は、ジェネリック Netlink ファミリーを使用する前に、まずそれを登録する必要があることです (これは通常の Netlink ファミリーにも当てはまります)。カーネルは、知らないファミリを処理できません。既存のファミリを使用していない限り、Netlink へのアプローチ方法はこれで決まります。
Generic Netlink Family は、カーネル モジュールに属します。これは、ユーザー空間クライアントがファミリーを作成できないことを意味します。つまり、クライアントを起動するだけではなく、ファミリを作成した直後にモジュールにメッセージを送信させることができます。これは、クライアントが自身をバインドしようとした時点でファミリが存在しないためです。
あなたがする必要があるのはこれです:
- モジュールの挿入中に、ファミリとマルチキャスト グループを作成して登録します。
- ユーザー空間クライアントを起動し、それ自体をファミリおよびマルチキャスト グループにバインドします。
- ある時点で (クライアントのバインド後)、カーネル モジュールにメッセージを送信させます。
- ユーザー空間クライアントがメッセージを受信します。
- モジュールが削除されると、ファミリの登録が解除されます。
あなたのコードの私のバージョンは次のとおりです。これがカーネルモジュールです。ご覧のとおり、2 秒ごとに実行されるタイマーでメッセージを繰り返し送信することにしました。これにより、クライアントを起動する時間ができます。
#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;
}