我试图实现的任务实际上非常简单(将字符串" TEST"多播到userland守护程序),但内核模块不编译。它会因错误而停止:
passing argument 4 of ‘genlmsg_multicast_allns’ makes integer from pointer without a cast [enabled by default]
但它不应该只是我定义的组播组吗?
以下是"澄清"的代码:
#include <linux/module.h>
#include <net/sock.h>
#include <linux/netlink.h>
#include <linux/skbuff.h>
#include <linux/string.h>
#include <net/netlink.h>
#include <net/genetlink.h>
struct sock *nl_sk = NULL;
static void daemon(void){
struct sk_buff *skb;
void* msg_head;
unsigned char *msg;
struct genl_family my_genl_family = {
.id = GENL_ID_GENERATE,
.hdrsize = 0,
.name = "family_name",
.version = 1,
.maxattr = 5
};
struct genl_multicast_group my_mc_group = {
.name = "mc_group",
};
msg = "TEST";
skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
msg_head = genlmsg_put(skb, 0, 0, &my_genl_family, 0, 21);
nla_put(skb, 0, sizeof(msg), msg);
genlmsg_end(skb, msg_head);
genlmsg_multicast_allns( &my_genl_family, skb, 0, my_mc_group, GFP_KERNEL);
}
static int __init hello_init(void)
{
printk("Entering: %s\n", __FUNCTION__);
printk(KERN_INFO "Calling main function with sockets\n");
struct netlink_kernel_cfg cfg = {
.groups = 1,
.flags = NL_CFG_F_NONROOT_RECV,
};
nl_sk = netlink_kernel_create(&init_net, NETLINK_GENERIC, &cfg);
daemon();
return 0;
}
static void __exit hello_exit(void)
{
printk(KERN_INFO "exiting hello module\n");
netlink_kernel_release(nl_sk);
}
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>
#include <linux/genetlink.h>
/*
* This function will be called for each valid netlink message received
* in nl_recvmsgs_default()
*/
static int my_func(struct nl_msg *msg, void *arg)
{
//struct nl_msg *nlmsg = nlmsg_alloc_size(GENL_HDRLEN+nla_total_size(sizeof(msg))+36);
printf("Test\n");
return 0;
}
int main(){
struct nl_sock *sk;
int gr_id;
/* Allocate a new socket */
sk = nl_socket_alloc();
/*
* Notifications do not use sequence numbers, disable sequence number
* checking.
*/
nl_socket_disable_seq_check(sk);
/*
* Define a callback function, which will be called for each notification
* received
*/
nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM, my_func, NULL);
/* Connect to netlink generic protocol */
nl_connect(sk, NETLINK_GENERIC);
gr_id = genl_family_get_id("family_name");
/* Subscribe to link notifications group */
nl_socket_add_memberships(sk, gr_id, 0);
/*
* Start receiving messages. The function nl_recvmsgs_default() will block
* until one or more netlink messages (notification) are received which
* will be passed on to my_func().
*/
while (1){
nl_recvmsgs_default(sk);
}
return 0;
}
答案 0 :(得分:3)
我想首先说我不是Netlink的忠实粉丝;我认为它设计得很差。也就是说,我认为我对这个问题有了确切的答案,所以就这样了。
您的核心问题是,在使用Generic 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;
}
答案 1 :(得分:1)
这不是netlink问题的直接答案,而是另一种解决方案。请参阅上面有关netlink限制的评论。
可以在Linux上使用UDP套接字在用户模式进程(如守护程序)和内核模式组件(如可加载模块)之间进行通信。
守护程序代码my_udp.c:
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
static int rcv_sock;
static int snd_sock;
static struct sockaddr_in rcv_addr_in;
static struct sockaddr_in snd_addr_in;
static pthread_t rcv_thread;
static void *rcv_thread_fn(void *data);
int my_udp_init(void)
{
int sendlen, receivelen;
int received = 0;
if ((rcv_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
perror("socket");
return -1;
}
if ((snd_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
perror("socket");
return -1;
}
memset(&rcv_addr_in, 0, sizeof(rcv_addr_in));
rcv_addr_in.sin_family = AF_INET;
rcv_addr_in.sin_addr.s_addr = inet_addr("127.0.0.1");
rcv_addr_in.sin_port = htons(MY_IN_PORT);
receivelen = sizeof(rcv_addr_in);
if (bind(rcv_sock, (struct sockaddr *) &rcv_addr_in, receivelen) < 0) {
perror("bind");
return -1;
}
memset(&snd_addr_in, 0, sizeof(snd_addr_in));
snd_addr_in.sin_family = AF_INET;
snd_addr_in.sin_addr.s_addr = inet_addr("127.0.0.1");
snd_addr_in.sin_port = htons(MY_OUT_PORT);
if (pthread_create(&rcv_thread, NULL, rcv_thread_fn, (void *)"rcv_thread")) {
return -ENOMEM;
}
return 0;
}
void my_udp_cleanup(void)
{
pthread_join(rcv_thread, NULL);
close(rcv_sock);
close(snd_sock);
}
int my_snd_msg(const char *buf, int size)
{
sendto(snd_sock, buf, size, 0, (struct sockaddr *)&snd_addr_in, sizeof(snd_addr_in));
return 0;
}
int my_rcv_msg(char *buf, int size)
{
int cnt = 0;
if ((cnt = recv(rcv_sock, buf, size, MSG_DONTWAIT)) < 0) {
if (errno == EAGAIN) {
/* This is ok in the non-blocking case. */
sleep(1);
return 0;
} else {
perror("recv");
return -1;
}
}
return cnt;
}
static void *rcv_thread_fn(void *data)
{
char buffer[64];
int cnt;
while (!g_stop) {
cnt = my_rcv_msg(buffer, 63);
if (cnt > 0) {
printf("message: %s\n", buffer);
}
}
pthread_exit(0);
}
内核模块代码k_udp.c:
#include <linux/module.h>
#include <linux/init.h>
#include <linux/in.h>
#include <net/sock.h>
#include <linux/skbuff.h>
#include <linux/delay.h>
#include <linux/inet.h>
#include <linux/kthread.h>
static struct work_struct rcv_worker;
static struct socket *in_socket = NULL;
static struct socket *out_socket = NULL;
static struct workqueue_struct *wq = NULL;
static struct task_struct *notify_thread = NULL;
void rcv_work_queue(struct work_struct *data)
{
int len;
printk(KERN_INFO "%s: *******\n", __func__);
while ((len = skb_queue_len(&in_socket->sk->sk_receive_queue)) > 0) {
struct sk_buff *skb = NULL;
skb = skb_dequeue(&in_socket->sk->sk_receive_queue);
printk("message len: %i message: %s\n", skb->len - 8, skb->data+8);
kfree_skb(skb);
}
}
static void cb_data(struct sock *sk, int bytes)
{
printk(KERN_INFO "%s: *******\n", __func__);
queue_work(wq, &rcv_worker);
}
void send_notification(char *text)
{
struct sockaddr_in to_addr;
struct msghdr msg;
struct iovec iov;
mm_segment_t oldfs;
int len = 0;
if (out_socket->sk == NULL) {
printk(KERN_ERR "%s: socket skbuff is null\n", __func__);
return;
}
iov.iov_base = text;
len = strlen(text);
iov.iov_len = len;
memset(&to_addr, 0, sizeof(to_addr));
to_addr.sin_family = AF_INET;
to_addr.sin_addr.s_addr = in_aton("127.0.0.1");
to_addr.sin_port = htons(MY_OUT_PORT);
msg.msg_flags = 0;
msg.msg_name = &to_addr;
msg.msg_namelen = sizeof(struct sockaddr_in);
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = NULL;
oldfs = get_fs();
set_fs(KERNEL_DS);
sock_sendmsg(out_socket, &msg, len);
set_fs(oldfs);
}
static int k_udp_notify_thread(void *data)
{
int i = 0;
while (!kthread_should_stop()) {
char buf[64];
sprintf(buf, "test from kernel%d\n", i++);
send_notification(buf);
msleep(1000);
}
return 0;
}
int k_udp_init(void)
{
struct sockaddr_in addr_out;
struct sockaddr_in addr_in;
int rc = 0;
printk("%s\n", __func__);
if (in_socket) {
printk(KERN_INFO "%s: socket already set up\n", __func__);
return 0;
}
if (sock_create(PF_INET, SOCK_DGRAM, IPPROTO_UDP, &in_socket) < 0) {
printk( KERN_ERR "%s: failed to create socket\n", __func__);
return -EIO;
}
addr_in.sin_family = AF_INET;
addr_in.sin_addr.s_addr = in_aton("127.0.0.1");
addr_in.sin_port = htons( (unsigned short)MY_IN_PORT);
rc = in_socket->ops->bind(in_socket, (struct sockaddr *)&addr_in, sizeof(addr_in));
if (rc) {
printk(KERN_ERR "%s: failed to bind\n", __func__);
sock_release(in_socket);
in_socket = NULL;
return -EIO;
}
in_socket->sk->sk_data_ready = cb_data;
if (sock_create(PF_INET, SOCK_DGRAM, IPPROTO_UDP, &out_socket) < 0) {
printk( KERN_ERR "%s: failed to create socket\n", __func__);
sock_release(in_socket);
in_socket = NULL;
return -EIO;
}
addr_out.sin_family = AF_INET;
addr_out.sin_addr.s_addr = in_aton("127.0.0.1");
addr_out.sin_port = htons( (unsigned short)MY_OUT_PORT);
rc = out_socket->ops->connect(out_socket, (struct sockaddr *)&addr_out, sizeof(addr_out), 0);
if (rc) {
printk(KERN_ERR "%s: failed to connect\n", __func__);
sock_release(in_socket);
in_socket = NULL;
sock_release(out_socket);
out_socket = NULL;
return -EIO;
}
notify_thread = kthread_create(k_udp_notify_thread, NULL, "k_notify_thread");
if (notify_thread) {
printk(KERN_INFO "%s: notify thread created\n", __func__);
wake_up_process(notify_thread);
} else {
printk(KERN_ERR "%s: failed to create notify thread\n", __func__);
}
INIT_WORK(&rcv_worker, rcv_work_queue);
wq = create_singlethread_workqueue("k_rcv_wq");
if (!wq) {
return -ENOMEM;
}
printk(KERN_INFO "%s: success\n", __func__);
return 0;
}
void k_udp_cleanup(void)
{
/* Should we check that the thread is still valid (hasn't exited)? */
if (notify_thread) {
kthread_stop(notify_thread);
notify_thread = NULL;
}
if (in_socket) {
sock_release(in_socket);
in_socket = NULL;
}
if (out_socket) {
sock_release(out_socket);
out_socket = NULL;
}
if (wq) {
flush_workqueue(wq);
destroy_workqueue(wq);
wq = NULL;
}
}
注意:我已经从我正在使用的代码中重命名了一些变量和函数名,因此您可能需要对编译进行更改(如果我遗漏了某些内容)。确保端口在用户/内核组件之间匹配。
以上代码源自网络上和Linux内核中的多个样本。