1

Disclaimer - I have to admit that it's the 1'st time I'm using this kernel interface (socket).

I'm currently working on a design of a kernel module that is based on a netlink socket .

I'm using Ubuntu14.04 and linux kernel 4.

As a starter, I wanted to make sure that I can use the netlink socket in both directions. I've written an application that does the following:

1) User send a message to kernel via the netlink socket.

2) Kernel, upon receiving the message – sends "ABCD" string message to a workqueue.

3) When the "ABCD" message is received by the workqueue, it calls a function (named - my_wq_function) which send it back to the user space via netlink socket.

4) In the user space I'm using a recvmsg function (blocking until a message is received) and displays the "ABCD" message.

My problem is that the return value from the recvmsg function is 20 (instead of 4), and the data itself (i.e. NLMSG_DATA) is empty. During the debug I tried to change the message to "ABCD1234" and got a return value of 24 bytes, however the data is still empty.

I also verified that my entire path until the point of sending the "ABCD" from kernel to the socket is OK. Not sure what I'm doing wrong here & will highly appreciate your help.

Thanks in advance, MotiC.

my code example can be found below:

User space code:

printf("netlink receiver thread started...\n");

            nlh_rcv = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));
            while(true) //endless loop on netlink socket
            {
            memset(nlh_rcv, 0, NLMSG_SPACE(MAX_PAYLOAD));
            iov_rcv.iov_base = (void *)nlh_rcv;

            iov_rcv.iov_len = nlh_rcv->nlmsg_len;

            msg_rcv.msg_name = (void *)&dest_addr;
            msg_rcv.msg_namelen = sizeof(dest_addr);
            msg_rcv.msg_iov = &iov;
            msg_rcv.msg_iovlen = 1;

            ret=recvmsg(sock_fd, &msg_rcv, 0);  

            printf("errno=%i bytes=%i message from kernel: %s\n",errno, ret, (char*)NLMSG_DATA(nlh_rcv));

            uint8_t mymsg[100];
            memcpy(mymsg, NLMSG_DATA(nlh_rcv), 100);
            printf("message from kernel: %s\n",mymsg);
    }

Kernel space code:

#include <linux/module.h>       /* Needed by all modules */
#include <linux/kernel.h>       /* Needed for KERN_INFO  */
#include <linux/init.h>         /* Needed for the macros */
#include <net/sock.h>
#include <linux/socket.h>
#include <linux/net.h>
#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/skbuff.h>
#include <linux/workqueue.h>

MODULE_LICENSE("GPL");


#include "rf_Kdriver_main.h"

//------ definitions ------------------------------------------------------------------------------------------------------------
#define NETLINK_USER 31
#define MAX_PAYLOAD 1024 /* maximum payload size*/

struct sock *nl_sk = NULL;

struct nlmsghdr *nlh;
struct nlmsghdr *nlh_out;

struct sk_buff *skb_out;

char buf_to_user[100];

int pid;


//------------------------------------------------------------------------------------------------------------------------------
struct workqueue_struct *my_wq;

typedef struct {
  struct work_struct my_work;
  uint8_t msg_to_pc[128];
  uint8_t msg_len;
} my_work_t;

my_work_t *work, *work2;

//-----------------------------------------------------------------------------------------------------------------------------
static void my_wq_function( struct work_struct *work)
{
            int res;
  my_work_t *my_work = (my_work_t *)work;

  skb_out = nlmsg_new(my_work->msg_len,0);
  if (!skb_out)
  {
            printk("Failed to allocate new skb\n");
            return;
  }
  nlh_out = nlmsg_put(skb_out, 0, 0, NLMSG_DONE,my_work->msg_len, 0);
  NETLINK_CB(skb_out).dst_group = 0;
  memcpy((char*)NLMSG_DATA(nlh_out), my_work->msg_to_pc , my_work->msg_len);

  printk( "dequeue message to pc=%s len=%i\n", (char*)NLMSG_DATA(nlh_out), (int)strlen((char*)NLMSG_DATA(nlh_out)));

  res = nlmsg_unicast(nl_sk, skb_out, pid);

  if (res<0)
            printk("Failed to send message from kernel to user\n");


  kfree( (void *)work );

  return;
}
//-----------------------------------------------------------------------------------------------------------------------------
int send_up_msg_to_workque(uint8_t msg_to_pc[], uint8_t msg_len)
{
  int ret=0;

  work = (my_work_t *)kmalloc(sizeof(my_work_t), GFP_KERNEL);
  if (work) {

    INIT_WORK( (struct work_struct *)work, my_wq_function );

    memcpy(work->msg_to_pc, msg_to_pc, msg_len);
    work->msg_len = msg_len;

    ret = queue_work( my_wq, /*(struct work_struct *)RR*/work );
    printk("kuku ret=%i msg=%s\n",ret,work->msg_to_pc);

  }
  return ret;
}
//------------------------------------------------------------------------------------------------------------------------------
static void netlink_recv_msg(struct sk_buff *skb)
{
    char *msg = "ABCD1234";

    printk(KERN_INFO "Entering: %s\n", __FUNCTION__);

    nlh=(struct nlmsghdr*)skb->data;
    printk(KERN_INFO "Netlink at kernel received msg payload: %s\n",(char*)NLMSG_DATA(nlh));
//rr
    pid = nlh->nlmsg_pid;

    send_up_msg_to_workque((uint8_t*) msg, strlen(msg));
}
//-------------------------------------------------------------------------------------------------------------------------------------

struct netlink_kernel_cfg cfg = {
    .input = netlink_recv_msg,
};

static int __init rf_driver_start(void)
{
            printk(KERN_INFO "Loading RF Driver module1...\n");

    my_wq = create_workqueue("my_queue");
            if (!my_wq)
            {
                        printk("Failed to create work queue\n");
            }

    printk("Entering: %s\n",__FUNCTION__);
    nl_sk = netlink_kernel_create(&init_net, NETLINK_USER, &cfg);
    if(!nl_sk)
    {
            printk(KERN_ALERT "Error creating socket.\n");
            return -10;
    }


            return 0;
}
//--------------------------------------------------------------------------------------------------------------
static void __exit rf_driver_end(void)
{
            netlink_kernel_release(nl_sk);
            flush_workqueue(my_wq);
            destroy_workqueue(my_wq);
            printk(KERN_INFO "RF Driver exit...\n");
}

module_init(rf_driver_start);
module_exit(rf_driver_end);
MotiC
  • 33
  • 1
  • 5
  • I used at the user space the function: char buf[100]; ret=recv(sock_fd, buf, 100, 0); instead of: ret=recvmsg(sock_fd, &msg_rcv, 0); and it works... does anyone have an idea of this strange behavior ? – MotiC Apr 06 '16 at 13:30

2 Answers2

0

Update,

I changed my user space function to:

char buf[100];
ret=recv(sock_fd, buf, 100, 0);

instead of:

ret=recvmsg(sock_fd, &msg_rcv, 0);

and it works...

does anyone have an idea regarding this strange behavior ?

Thanks.

MotiC
  • 33
  • 1
  • 5
0

Can you please paste complete userspace code. I guess 'len' int this code is the issue:

    memset(nlh_rcv, 0, NLMSG_SPACE(MAX_PAYLOAD));

    iov_rcv.iov_len = nlh_rcv->nlmsg_len;   << check to what value is it getting initialized.
Pramod
  • 371
  • 4
  • 13