用户空间netlink套接字从内核空间

时间:2016-04-05 16:34:33

标签: linux-kernel linux-device-driver netlink

免责声明 - 我必须承认,这是我使用此内核接口(套接字)的第一次。

我目前正在设计一个基于netlink套接字的内核模块。

我正在使用Ubuntu14.04和linux内核4。

作为初学者,我想确保我可以在两个方向上使用netlink套接字。 我编写了一个执行以下操作的应用程序:

1)用户通过netlink socket向内核发送消息。

2)内核在收到消息后发送" ABCD"字符串消息到工作队列。

3)当" ABCD"消息由工作队列接收,它调用一个函数(名为my_wq_function),通过netlink socket将其发送回用户空间。

4)在用户空间中,我使用recvmsg功能(阻止直到收到消息)并显示" ABCD"消息。

我的问题是recvmsg函数的返回值是20(而不是4),数据本身(即NLMSG_DATA)是空的。 在调试期间,我试图将消息更改为" ABCD1234"并获得24字节的返回值,但数据仍为空。

我还验证了我的整个路径,直到发送" ABCD"从内核到套接字是可以的。 我不知道我在这里做错了什么。非常感谢你的帮助。

提前致谢,MotiC。

我的代码示例可以在下面找到:

用户空间代码:

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);
    }

内核空间代码:

#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);

2 个答案:

答案 0 :(得分:0)

更新

我将用户空间功能更改为:

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

而不是:

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

它有效......

有没有人对这种奇怪的行为有所了解?

感谢。

答案 1 :(得分:0)

可以请粘贴完整的用户空间代码。 我想'len'这个代码就是问题所在:

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

    iov_rcv.iov_len = nlh_rcv->nlmsg_len;   << check to what value is it getting initialized.