Reputation: 51
The task I am trying to achieve is actually quite simple (multicast the string "TEST" to a userland daemon), but the kernel module doesn't compile. It stops with the error:
passing argument 4 of ‘genlmsg_multicast_allns’ makes integer from pointer without a cast [enabled by default]
But shouldn't it just be the multicast group I defined?
Here is the code for "clarification":
#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");
Thanks for your help.
EDIT
This is the client side code:
#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;
}
Upvotes: 5
Views: 4059
Reputation: 1138
Your core problem is that before using a Generic Netlink family, you first have to register it (this also applies to normal Netlink families). The kernel cannot handle families it doesn't know. Unless you're using an already existing family, this shapes the way you have to approach Netlink.
A Generic Netlink Family belongs to a kernel module. This means a userspace client cannot create a family. In turn, this means you can't just start the client, then have the module send the message right after it creates the family. This is because the family doesn't exist at the moment the client wanted to bind itself to it.
What you need to do is this:
My version of your code follows. This is driver.c
, the kernel module. As you can see, I decided to send the message repeatedly on a timer that runs every two seconds. This gives you time to start the client:
#include <linux/kernel.h>
#include <linux/module.h>
#include <net/genetlink.h>
#include <linux/timer.h>
static struct timer_list timer;
static const int repeat_ms = 2000;
/**
* 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.
* Among other reasons, the designers of Netlink want you to use these instead
* of dumping raw data to the packet payload because it's designed to prevent
* alignment problems for you.
* (http://www.catb.org/esr/structure-packing/)
*/
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,
.doit = hello, /* The dummy function we defined above. */
.dumpit = NULL,
},
};
/**
* Your multicast group. Choose a likely unique name.
*/
struct genl_multicast_group groups[] = {
{ .name = "PotatoGroup" },
};
/**
* A Generic Netlink family is a group of listeners who can and want to speak
* your "language" (ie. your set of Attributes).
* Anyone who wants to hear your messages needs to register to the same family
* as you.
* (And because we're using multicast, they will have to register to the same
* multicast group as well.)
*/
struct genl_family family = {
.hdrsize = 0,
.name = "PotatoFamily",
.version = 1,
.maxattr = __ATTR_MAX,
.policy = policies,
.ops = ops,
.n_ops = ARRAY_SIZE(ops),
.mcgrps = groups,
.n_mcgrps = ARRAY_SIZE(groups),
};
void send_multicast(struct timer_list *t)
{
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("Adding Generic Netlink header to the 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 'hello' attribute.\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 'foo' attribute.\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:
// Reschedule the timer to call this function again in repeat_ms
// milliseconds
mod_timer(t, jiffies + msecs_to_jiffies(repeat_ms));
}
static int init_socket(void)
{
int error;
pr_info("Registering family.\n");
error = genl_register_family(&family);
if (error)
pr_err("Family registration failed: %d\n", error);
return error;
}
static void initialize_timer(void)
{
pr_info("Starting timer.\n");
// Initialize the timer and assign the callback function
timer_setup(&timer, send_multicast, 0);
// Set the timer to expire in repeat_ms milliseconds from now
mod_timer(&timer, jiffies + msecs_to_jiffies(repeat_ms));
}
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)
{
// Make sure to delete the timer when exiting your module
del_timer(&timer);
genl_unregister_family(&family);
pr_info("Hello removed.\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
And this is app.c
, the userspace client:
#include <netlink/netlink.h>
#include <netlink/socket.h>
#include <netlink/msg.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/ctrl.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;
}
Here's also a sample Makefile (please verify your directories):
KERNEL_HEADERS := /lib/modules/$(shell uname -r)/build
LIBNL_INC := /usr/include/libnl3
LIBNL_LIB := /usr/lib/x86_64-linux-gnu
obj-m += driver.o
CC := gcc
CFLAGS := -I$(KERNEL_HEADERS)
APPFLAGS := -I$(LIBNL_INC)
LDFLAGS := -L$(LIBNL_LIB)
LDLIBS := -lnl-3 -lnl-genl-3
.PHONY: all driver app clean
all: driver app
driver:
$(MAKE) -C $(KERNEL_HEADERS) M=$(shell pwd) modules
app: app.c
$(CC) $(APPFLAGS) $(LDFLAGS) -o app app.c $(LDLIBS)
clean:
$(MAKE) -C $(KERNEL_HEADERS) M=$(shell pwd) clean
rm -f app
Upvotes: 6
Reputation: 1041
This is not a direct answer to the netlink question, but an alternative solution. See comments above about netlink limitations.
UDP sockets can be used on Linux to communicate between a user mode process, such as a daemon, and a kernel mode component such as a loadable module.
Daemon code 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);
}
Kernel module code 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;
}
}
Note: I've renamed some of the variables and function names from the code I'm using, so you may need to make changes to compile (if I missed something). Be sure to make the ports match between user/kernel components.
The above code was derived from multiple samples on the net and within the Linux kernel.
Upvotes: 1