1

I have refered to the following source by Yd Ahhrk

Multicast from kernel to user space via Netlink in C

the module is insmod correctly , lsmod|less can see it is there , but while try to run user space application , I have the error:

seotsockopt < 0

In user space application , it is doing the group error :

if (setsockopt(sock, 270, NETLINK_ADD_MEMBERSHIP, &group, sizeof(group)) < 0) {
    printf("seotsockopt < 0\n");
    return -1;
}

While I google other sources , look like setsockopt and NETLINK_ADD_MEMBERSHIP doing the exact right thing , except for Yd Ahhrk tested it in 3.13 , I test it in 3.10 , I have no idea how to avoid the error and make it work .

Edit :

The following is what in my code : xyzkernel.c

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/netlink.h>
#include <net/netlink.h>
#include <net/net_namespace.h>

/* Protocol family, consistent in both kernel prog and user prog. */
#define MYPROTO NETLINK_USERSOCK
/* Multicast group, consistent in both kernel prog and user prog. */
#define MYGRP 1

static struct sock *nl_sk = NULL;

static void send_to_user(void)
{
    struct sk_buff *skb;
    struct nlmsghdr *nlh;
    char *msg = "Hello from kernel";
    int msg_size = strlen(msg) + 1;
    int res;

    pr_info("Creating skb.\n");
    skb = nlmsg_new(NLMSG_ALIGN(msg_size + 1), GFP_KERNEL);
    if (!skb) {
        pr_err("Allocation failure.\n");
        return;
    }

    nlh = nlmsg_put(skb, 0, 1, NLMSG_DONE, msg_size + 1, 0);
    strcpy(nlmsg_data(nlh), msg);

    pr_info("Sending skb.\n");
    res = nlmsg_multicast(nl_sk, skb, 0, MYGRP, GFP_KERNEL);
    if (res < 0)
        pr_info("nlmsg_multicast() error: %d\n", res);
    else
        pr_info("Success.\n");
}

static int __init marskernel_init(void)
{
    pr_info("Inserting marskernel module.\n");

    nl_sk = netlink_kernel_create(&init_net, MYPROTO, NULL);
    if (!nl_sk) {
        pr_err("Error creating socket.\n");
        return -10;
    }

    send_to_user();

    netlink_kernel_release(nl_sk);
    return 0;
}

static void __exit marskernel_exit(void)
{
    pr_info("Exiting marskernel module.\n");
}

module_init(marskernel_init);
module_exit(marskernel_exit);

MODULE_LICENSE("GPL");

and the client.cpp

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <unistd.h>

/* Protocol family, consistent in both kernel prog and user prog. */
#define MYPROTO NETLINK_USERSOCK
/* Multicast group, consistent in both kernel prog and user prog. */
#define MYMGRP 1

int open_netlink(void)
{
    int sock;
    struct sockaddr_nl addr;
    int group = MYMGRP;

    sock = socket(AF_NETLINK, SOCK_RAW, MYPROTO);
    if (sock < 0) {
        printf("sock < 0.\n");
        return sock;
    }

    memset((void *) &addr, 0, sizeof(addr));
    addr.nl_family = AF_NETLINK;
    addr.nl_pid = getpid();
    /* This doesn't work for some reason. See the setsockopt() below. */
    /* addr.nl_groups = MYMGRP; */

    if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
        printf("bind < 0.\n");
        return -1;
    }

    /*
     * 270 is SOL_NETLINK. See
     * http://lxr.free-electrons.com/source/include/linux/socket.h?v=4.1#L314
     * and
     * https://stackoverflow.com/questions/17732044/
     */
    /*
    if (setsockopt(sock, 270, NETLINK_ADD_MEMBERSHIP, &group, sizeof(group)) < 0) {
        printf("setsockopt < 0\n");
        return -1;
    }
    */

    return sock;
}
void read_event(int sock)
{
    struct sockaddr_nl nladdr;
    struct msghdr msg;
    struct iovec iov;
    char buffer[65536];
    int ret;

    iov.iov_base = (void *) buffer;
    iov.iov_len = sizeof(buffer);
    msg.msg_name = (void *) &(nladdr);
    msg.msg_namelen = sizeof(nladdr);
    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;

    printf("Ok, listening.\n");
    ret = recvmsg(sock, &msg, 0);
    if (ret < 0)
        printf("ret < 0.\n");
    else
        printf("Received message payload: %s\n", NLMSG_DATA((struct nlmsghdr *) &buffer));
}

int main(int argc, char *argv[])
{
    int nls;

    nls = open_netlink();
    if (nls < 0)
        return nls;

    while (1)
        read_event(nls);

    return 0;
}

When I marked both

/* addr.nl_groups = MYMGRP; */

and

/*
    if (setsockopt(sock, 270, NETLINK_ADD_MEMBERSHIP, &group, sizeof(group)) < 0) {
        printf("setsockopt < 0\n");
        return -1;
    }
*/

won't encountered run time error , but make no sense since MYGRP 1 not used anymore , but if I unmarked addr.nl_groups = MYMGRP then bind will error , unmarked setsockopt then setsockopt will error ..... I have no idea how to go on for this case !!

Community
  • 1
  • 1
barfatchen
  • 1,630
  • 2
  • 24
  • 48
  • Thanks for your question, I was following your example to create my own netlinks. However, in my case I have 2 modulus, and each has to open its own netlink socket to user space. My problem is that whichever module being loaded the second, it fails on netlink_kernel_create. I am probably missing something, do you know if it is possible to create multiple sockets with NETLINK_USERSOCK family? Thanks! – kirbo Oct 06 '16 at 19:24

1 Answers1

2

I had a similar problem and I think I may have been working of the same or similar example. There is not a huge amount I can tell you without seeing all of your code. I have can pass on a couple of things I did to make multicast work though.

  1. Define your group as 1 in both the kernel and userspace code.
  2. Remove setsockopt() altogether.

Without seeing the rest of your code, I cannot provide more instruction.

Good luck

Here is the code I used. It should help you along the way. However, my Kernel module does not have an exit function for security reasons.

Kernel Code

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/netlink.h>
#include <net/netlink.h>
#include <net/sock.h>
#include <net/net_namespace.h>
#include <linux/skbuff.h>

#define MY_GROUP    1

struct sock* socket;
struct sk_buff* socket_buff;
char *log;

void nl_data_ready(struct sock *sk, int len)
{
  nlmsg_free(socket_buff);
}
static void send_to_user(char *message)
{
    struct nlmsghdr *nlsk_mh;                                                                         
    char* msg = message;
    int res;                                                                                            

    socket_buff = nlmsg_new(256, GFP_KERNEL);                                                                                                                                                                                                                                                 
    nlsk_mh = nlmsg_put(socket_buff, 0, 0, NLMSG_DONE, strlen(msg), 0);                       
    NETLINK_CB(socket_buff).pid = 0;    // kernel pid                                                   
    NETLINK_CB(socket_buff).dst_group = MY_GROUP;                                                     
    strcpy(nlmsg_data(nlsk_mh), msg);                                                                

    res = nlmsg_multicast(socket, socket_buff, 0, MY_GROUP, GFP_KERNEL);

    if(res < 0)
    {
        printk("Multicast not sent: res < 0\n");
        return 0;
    }
    else
    {
        printk("Multicast Sent\n");
    }

}
void netlink_test(char *buf)
{
    socket = netlink_kernel_create(&init_net, NETLINK_USERSOCK, MY_GROUP, nl_data_ready, NULL, THIS_MODULE);
    if (!socket) {
        printk("Error creating socket.\n");
        return -10;
    }
    else
    {
        printk("\n\nSOCKET WAS CREATED SUCCESSFULLY\n\n");
    }

    printk("The message to be sent to the user is: %s", buf);
    send_to_user(buf);

    netlink_kernel_release(socket);

    return 0;


}

UserSpace Code

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <unistd.h>
#include <errno.h>
#include <unistd.h>
#define MAX_PAYLOAD 1024 /* maximum payload size*/
#define MY_GROUP    1

int main(void)
{
    int sock_fd;
    struct sockaddr_nl user_sockaddr;
    struct nlmsghdr *nl_msghdr;
    struct msghdr msghdr;
    struct iovec iov;

    char* kernel_msg;

    sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_USERSOCK);
    if(sock_fd<0)
    {
        printf("Error creating socket because: %s\n", strerror(errno));
        return -1;
    }


    memset(&user_sockaddr, 0, sizeof(user_sockaddr));
    user_sockaddr.nl_family = AF_NETLINK;
    user_sockaddr.nl_pid = getpid();
    user_sockaddr.nl_groups = MY_GROUP;

    bind(sock_fd, (struct sockaddr*)&user_sockaddr, sizeof(user_sockaddr));
    while (1) {
        nl_msghdr = (struct nlmsghdr*) malloc(NLMSG_SPACE(1024));
        memset(nl_msghdr, 0, NLMSG_SPACE(1024));

        iov.iov_base = (void*) nl_msghdr;
        iov.iov_len = NLMSG_SPACE(1024);

        msghdr.msg_name = (void*) &user_sockaddr;
        msghdr.msg_namelen = sizeof(user_sockaddr);
        msghdr.msg_iov = &iov;
        msghdr.msg_iovlen = 1;

        printf("Waiting to receive message\n");
        recvmsg(sock_fd, &msghdr, 0);

        kernel_msg = (char*)NLMSG_DATA(nl_msghdr);
        printf("Kernel message: %s\n", kernel_msg); // print to android logs
    }

    close(sock_fd);
}

Also, I believe that the netlink_kernel_create() function has different implementations in different Kernel versions. So some functions I have in here might be different for your Kernel version (depending on what it is).

Also, I think the Kernel code requires a callback function. I did not notice this in your code. The code should work but the article I linked to is much more useful for figuring out exactly what is going on.

Cheers

S. Gill
  • 96
  • 8
  • , thanks for kindly reply , I have more information above just now, what problem I had is that setsockopt add group , if I remove setsockopt , I don't know how to NETLINK_ADD_MEMBERSHIP , addr.nl_groups = MYMGRP; will cause bind error . – barfatchen May 04 '16 at 23:44
  • I do not have enough reputation to comment on your question but I will comment here instead. There is a very good article/tutorial that I used here: [link](http://www.linuxjournal.com/node/7356/print) that explains both unicast and multicast thoroughly. I defined addr.nl_groups = 1, as should be the case in multicast. – S. Gill May 05 '16 at 09:40
  • Thank you , you help a lot . – barfatchen May 06 '16 at 00:20