c - 通过通用网络链接从内核到用户空间接收具有 2 个以上字段的结构时出现问题

标签 c linux linux-kernel netlink

我正在尝试从 LKM 向用户空间发送/接收结构(反之亦然),基于此示例 http://www.electronicsfaq.com/2014/02/generic-netlink-sockets-example-code.html .

我向内核发送一个结构。它接收结构 ok,处理信息,并将结构发送回用户空间。但是,如果该结构有 2 个字段,我可以发送和接收,但是当我再添加一个字段时,我会收到一个空结构。自 2 天前以来,我已经探索了一些变体,但一直没有成功。我很困惑,我不确定问题出在哪里?这是代码:

nl_user.c:

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <poll.h> 
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <signal.h>
#include "global.h"
#include <linux/genetlink.h>
#define GENLMSG_DATA(glh) ((void *)(NLMSG_DATA(glh) + GENL_HDRLEN))
#define GENLMSG_PAYLOAD(glh) (NLMSG_PAYLOAD(glh, 0) - GENL_HDRLEN)
#define NLA_DATA(na) ((void *)((void*)(na) + NLA_HDRLEN))
//Variables used for netlink
int nl_fd;  //netlink socket's file descriptor
struct sockaddr_nl nl_address; //netlink socket address
int nl_family_id; 
int nl_rxtx_length; //Number of bytes sent or received via send() or recv()
struct nlattr *nl_na; //pointer to netlink attributes structure within the payload 
Response p;
struct { 
    struct nlmsghdr n;
    struct genlmsghdr g;
    char buf[256];
} nl_request_msg, nl_response_msg;


int main(void) {

struct {  
   struct nlmsghdr n;
   struct genlmsghdr g;
   Response buf;
} my_nl_request_msg, my_nl_response_msg;

//Step 1: Open the socket. Note that protocol = NETLINK_GENERIC
 nl_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
 if (nl_fd < 0) {
    perror("socket()");
  return -1;
 }

 //Step 2: Bind the socket.
 memset(&nl_address, 0, sizeof(nl_address));
 nl_address.nl_family = AF_NETLINK;
 nl_address.nl_groups = 0;

 if (bind(nl_fd, (struct sockaddr *) &nl_address, sizeof(nl_address)) < 0) {
     perror("bind()");
     close(nl_fd);
     return -1;
 }

//Step 3. Resolve the family ID corresponding to the string "CONTROL_EXMPL"

//Populate the netlink header
nl_request_msg.n.nlmsg_type = GENL_ID_CTRL;
nl_request_msg.n.nlmsg_flags = NLM_F_REQUEST;
nl_request_msg.n.nlmsg_seq = 0;
nl_request_msg.n.nlmsg_pid = getpid();
nl_request_msg.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);

//Populate the payload's "family header" : which in our case is genlmsghdr
nl_request_msg.g.cmd = CTRL_CMD_GETFAMILY;
nl_request_msg.g.version = 0x1;

//Populate the payload's "netlink attributes"
nl_na = (struct nlattr *) GENLMSG_DATA(&nl_request_msg);
nl_na->nla_type = CTRL_ATTR_FAMILY_NAME;
nl_na->nla_len = strlen("CONTROL_EXMPL") + 1 + NLA_HDRLEN;
strcpy(NLA_DATA(nl_na), "CONTROL_EXMPL");     
nl_request_msg.n.nlmsg_len += NLMSG_ALIGN(nl_na->nla_len);
memset(&nl_address, 0, sizeof(nl_address));
nl_address.nl_family = AF_NETLINK;

//Send the family ID request message to the netlink controller
nl_rxtx_length = sendto(nl_fd, (char *) &nl_request_msg, nl_request_msg.n.nlmsg_len,
   0, (struct sockaddr *) &nl_address, sizeof(nl_address));
if (nl_rxtx_length != nl_request_msg.n.nlmsg_len) {
   perror("sendto()");
   close(nl_fd);
return -1;
}

//Wait for the response message
nl_rxtx_length = recv(nl_fd, &nl_response_msg, sizeof(nl_response_msg), 0);
if (nl_rxtx_length < 0) {
    perror("recv()");
    return -1;
}

//Validate response message
if (!NLMSG_OK((&nl_response_msg.n), nl_rxtx_length)) {
    fprintf(stderr, "family ID request : invalid message\n");
    return -1;
}
if (nl_response_msg.n.nlmsg_type == NLMSG_ERROR) { //error
    fprintf(stderr, "family ID request : receive error\n");
    return -1;
}

//Extract family ID
nl_na = (struct nlattr *) GENLMSG_DATA(&nl_response_msg);
nl_na = (struct nlattr *) ((char *) nl_na + NLA_ALIGN(nl_na->nla_len));
if (nl_na->nla_type == CTRL_ATTR_FAMILY_ID) {
    nl_family_id = *(__u16 *) NLA_DATA(nl_na);
}


//Step 4. Send own custom message
memset(&my_nl_request_msg, 0, sizeof(my_nl_request_msg));
memset(&my_nl_response_msg, 0, sizeof(my_nl_response_msg));

my_nl_request_msg.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
my_nl_request_msg.n.nlmsg_type = nl_family_id;
my_nl_request_msg.n.nlmsg_flags = NLM_F_REQUEST;
my_nl_request_msg.n.nlmsg_seq = 60;
my_nl_request_msg.n.nlmsg_pid = getpid();
my_nl_request_msg.g.cmd = 1; //corresponds to DOC_EXMPL_C_ECHO;

nl_na = (struct nlattr *) GENLMSG_DATA(&my_nl_request_msg);
nl_na->nla_type = 2; // corresponds to DOC_EXMPL_A_MSG
//nl_na->nla_len = sizeof(MESSAGE_TO_KERNEL)+NLA_HDRLEN; //Message length
//memcpy(NLA_DATA(nl_na), MESSAGE_TO_KERNEL, sizeof(MESSAGE_TO_KERNEL));
p.dataSize = 32;
nl_na->nla_len = sizeof(p)+NLA_HDRLEN; //Message length

memcpy(NLA_DATA(nl_na), &p, sizeof(p));

my_nl_request_msg.n.nlmsg_len += NLMSG_ALIGN(nl_na->nla_len);


memset(&nl_address, 0, sizeof(nl_address));
nl_address.nl_family = AF_NETLINK;


//Send the custom message 
nl_rxtx_length = sendto(nl_fd, &my_nl_request_msg,my_nl_request_msg.n.nlmsg_len, 0, (struct sockaddr *) &nl_address, sizeof(nl_address));
if (nl_rxtx_length != my_nl_request_msg.n.nlmsg_len) {
    perror("sendto()");
    close(nl_fd);
    return -1;
}
printf("Sent to kernel: %d\n",p.dataSize);

//Receive reply from kernel
nl_rxtx_length = recv(nl_fd, &my_nl_response_msg, sizeof(my_nl_response_msg), 0);
if (nl_rxtx_length < 0) {
    perror("recv()");
    return -1;
}

//Validate response message
if (my_nl_response_msg.n.nlmsg_type == NLMSG_ERROR) { //Error
    printf("Error while receiving reply from kernel: NACK Received\n");
    close(nl_fd);
    return -1;
}
if (nl_rxtx_length < 0) {
    printf("Error while receiving reply from kernel\n");
    close(nl_fd);
    return -1;
}
if (!NLMSG_OK((&my_nl_response_msg.n), nl_rxtx_length)) {
    printf("Error while receiving reply from kernel: Invalid Message\n");
    close(nl_fd);
 return -1;
}

//Parse the reply message
nl_rxtx_length = GENLMSG_PAYLOAD(&my_nl_response_msg.n);
nl_na = (struct nlattr *) GENLMSG_DATA(&my_nl_response_msg);
Response *r = (Response *)NLA_DATA(nl_na);
printf("Kernel replied: %d\n",r->dataSize);

//Step 5. Close the socket and quit
close(nl_fd);
return 0;

nl_kernel.c:

#include "global.h"
#include <net/genetlink.h>
#include <linux/module.h>
#include <linux/kernel.h>

/* attributes (variables):
* the index in this enum is used as a reference for the type,
* userspace application has to indicate the corresponding type
* the policy is used for security considerations 
*/
enum {
   DOC_EXMPL_A_UNSPEC,
   DOC_EXMPL_A_MSG,
   DOC_EXMPL_A_MSG2,
 __DOC_EXMPL_A_MAX,
};
#define DOC_EXMPL_A_MAX (__DOC_EXMPL_A_MAX - 1)

static struct nla_policy doc_exmpl_genl_policy[DOC_EXMPL_A_MAX + 1] = {
  [DOC_EXMPL_A_MSG] = { .type = NLA_NUL_STRING },
  [DOC_EXMPL_A_MSG2] = { .type = NLA_UNSPEC, .len = sizeof(Response) },
};

#define VERSION_NR 1
//family definition
static struct genl_family doc_exmpl_gnl_family = {
  .id = GENL_ID_GENERATE,         //Genetlink should generate an id
  .hdrsize = 0,
  .name = "CONTROL_EXMPL",
  .version = VERSION_NR,          //Version number  
  .maxattr = DOC_EXMPL_A_MAX,
};

/* commands: enumeration of all commands (functions), 
 * used by userspace application to identify command to be executed
*/
enum {
  DOC_EXMPL_C_UNSPEC,
  DOC_EXMPL_C_ECHO,
  __DOC_EXMPL_C_MAX,
};
#define DOC_EXMPL_C_MAX (__DOC_EXMPL_C_MAX - 1)


//An echo command, receives a message, prints it and sends another message back
int doc_exmpl_echo(struct sk_buff *skb_2, struct genl_info *info) {
struct nlattr *na;
struct sk_buff *skb;
int rc;
void *msg_head;
char * mydata;
Response *req;

if (info == NULL) {
  goto out;
}

if (info->attrs[DOC_EXMPL_A_MSG]) {
    na = info->attrs[DOC_EXMPL_A_MSG];
    mydata = (char *)nla_data(na);
    //req = (Response *)nla_data(na);
    if (req == NULL) {
        printk("error while receiving data\n");
    } else {
        //printk("received: %d\n", req->dataSize);
        printk("received: %s\n", mydata);
    }
 } 
 else if(info->attrs[DOC_EXMPL_A_MSG2]){
    na = info->attrs[DOC_EXMPL_A_MSG2];
    req = (Response *)nla_data(na);
    if (req == NULL) {
        printk("error while receiving data\n");
    } else {
        printk("received2: %d\n", req->dataSize);
    }

 }else {
    printk("no info->attrs %i - %i \n", DOC_EXMPL_A_MSG, DOC_EXMPL_A_MSG2);
 }

//Send a message back
//Allocate some memory, since the size is not yet known use NLMSG_GOODSIZE
 skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
 if (skb == NULL) {
   goto out;
 }

//Create the message headers
/* arguments of genlmsg_put: 
   struct sk_buff *, 
   int (sending) pid, 
   int sequence number, 
   struct genl_family *, 
   int flags, 
   u8 command index (why do we need this?)
*/
 msg_head = genlmsg_put(skb, 0, info->snd_seq+1, &doc_exmpl_gnl_family, 0, DOC_EXMPL_C_ECHO);
 if (msg_head == NULL) {
   rc = -ENOMEM;
   goto out;
 }

req->dataSize *= 10;
printk("req %d\n", req->dataSize);

//Add a DOC_EXMPL_A_MSG attribute (actual value to be sent)
rc = nla_put(skb, DOC_EXMPL_A_MSG2, sizeof(req),req);
if (rc != 0) {
   goto out;
}

//Finalize the message
genlmsg_end(skb, msg_head);

//Send the message back
rc = genlmsg_unicast(genl_info_net(info), skb,info->snd_pid );
if (rc != 0) {
  goto out;
}
return 0;

out:
  printk("An error occured in doc_exmpl_echo:\n");
  return 0;
}

//Commands: mapping between the command enumeration and the actual function
struct genl_ops doc_exmpl_gnl_ops_echo = {
   .cmd = DOC_EXMPL_C_ECHO,
   .flags = 0,
   .policy = doc_exmpl_genl_policy,
   .doit = doc_exmpl_echo,
   .dumpit = NULL,
};

static int __init gnKernel_init(void) {
  int rc;
  printk("Generic Netlink Example Module inserted.\n");

 //Register the new family
 rc = genl_register_family(&doc_exmpl_gnl_family);
 if (rc != 0) {
   goto failure;
 }
 //Register functions (commands) of the new family
 rc = genl_register_ops(&doc_exmpl_gnl_family, &doc_exmpl_gnl_ops_echo);
   if (rc != 0) {
   printk("Register ops: %i\n",rc);
   genl_unregister_family(&doc_exmpl_gnl_family);
   goto failure;
 }
return 0; 
failure:
    printk("An error occured while inserting the generic netlink example module\n");
    return -1;
}

static void __exit gnKernel_exit(void) {
   int ret;
   printk("Generic Netlink Example Module unloaded.\n");

   //Unregister the functions
   ret = genl_unregister_ops(&doc_exmpl_gnl_family, &doc_exmpl_gnl_ops_echo);
   if(ret != 0) {
   printk("Unregister ops: %i\n",ret);
   return;
}

//Unregister the family
ret = genl_unregister_family(&doc_exmpl_gnl_family);
if(ret !=0) {
   printk("Unregister family %i\n",ret);
 }
}

module_init(gnKernel_init);
module_exit(gnKernel_exit);
MODULE_LICENSE("GPL");

global.h

#ifndef __GLOBAL_H
#define __GLOBAL_H

typedef struct _Response Response;

struct _Response
{
  //int index;
  int dataSize;     /* ammount of data in bytes of the response */
  char data[4096];
};

#endif

注意:我在 Oracle VB 中使用的是 Debian 2.6.32-5(这是必需的)。

最佳答案

内核部分的这一行是主要问题。 Sizeof 指针将为 8(在 64 位机器上)或 4。您只发送前几个字节。

rc = nla_put(skb, DOC_EXMPL_A_MSG2, sizeof(req),req);

应该是“sizeof(*req)”。我试过了,但仍然存在数据偏移几个字节的问题......我猜其中一些宏没有按照你认为的那样做。

下面是我刚才做的一个例子,我把它改成了像你这样的结构,它按预期工作。在我看来,这段代码更加清晰。

netlinkKernel.h

#include "global.h"
#include <linux/module.h>
#include <net/sock.h>
#include <linux/netlink.h>
#include <linux/skbuff.h>


#define NETLINK_USER 31

struct sock *nl_sk = NULL;

static void hello_nl_recv_msg(struct sk_buff *skb) {

struct nlmsghdr *nlh;
int pid;
struct sk_buff *skb_out;
int msg_size;
int res;
Response *req;

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

msg_size= sizeof(Response);

nlh=(struct nlmsghdr*)skb->data;

req = (Response*)NLMSG_DATA(nlh);

printk("Recieved from Userspace:\n"); 
printk("index %d\n", req->index);
printk("dataSize %d\n", req->dataSize);
printk("data: %s\n", req->data);
printk("test2 %d\n", req->test2);
printk("test3 %d\n", req->test3);
printk("test4 %d\n", req->test4);

pid = nlh->nlmsg_pid; /*pid of sending process */

skb_out = nlmsg_new(msg_size,0);

if(!skb_out)
{

    printk(KERN_ERR "Failed to allocate new skb\n");
    return;

} 
nlh=nlmsg_put(skb_out,0,0,NLMSG_DONE,msg_size,0);  
NETLINK_CB(skb_out).dst_group = 0; /* not in mcast group */

req->index *= 100;
req->dataSize *= 100;
strcpy(req->data , "Data from Kernel");
req->test2 *= 100;
req->test3 *= 100;
req->test4 *= 100;

printk("Sending to Userspace:\n"); 
printk("index %d\n", req->index);
printk("dataSize %d\n", req->dataSize);
printk("data: %s\n", req->data);
printk("test2 %d\n", req->test2);
printk("test3 %d\n", req->test3);
printk("test4 %d\n", req->test4);

memcpy(NLMSG_DATA(nlh), req, sizeof(Response));

res=nlmsg_unicast(nl_sk,skb_out,pid);

if(res<0)
    printk(KERN_INFO "Error while sending bak to user\n");
}

static int __init hello_init(void) {

printk("Entering: %s\n",__FUNCTION__);
// This is for 3.6 kernels and above.
struct netlink_kernel_cfg cfg = {
    .input = hello_nl_recv_msg,
};

nl_sk = netlink_kernel_create(&init_net, NETLINK_USER, &cfg);
//nl_sk = netlink_kernel_create(&init_net, NETLINK_USER, 0, hello_nl_recv_msg,NULL,THIS_MODULE);
if(!nl_sk)
{

    printk(KERN_ALERT "Error creating socket.\n");
    return -10;

}

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

netlinkUser.c

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

#define NETLINK_USER 31

#define MAX_PAYLOAD 8192 /* maximum payload size*/
struct sockaddr_nl src_addr, dest_addr;
struct nlmsghdr *nlh = NULL;
struct iovec iov;
int sock_fd;
struct msghdr msg;
Response p;
Response *req;

int main()
{
sock_fd=socket(PF_NETLINK, SOCK_RAW, NETLINK_USER);
if(sock_fd<0)
return -1;

memset(&src_addr, 0, sizeof(src_addr));
src_addr.nl_family = AF_NETLINK;
src_addr.nl_pid = getpid(); /* self pid */

bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr));

memset(&dest_addr, 0, sizeof(dest_addr));
memset(&dest_addr, 0, sizeof(dest_addr));
dest_addr.nl_family = AF_NETLINK;
dest_addr.nl_pid = 0; /* For Linux Kernel */
dest_addr.nl_groups = 0; /* unicast */

nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));
memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));
nlh->nlmsg_len = NLMSG_LENGTH(MAX_PAYLOAD);
nlh->nlmsg_pid = getpid();
nlh->nlmsg_flags = 0;

p.index = 1;
p.dataSize = 2;
strcpy(p.data , "Data from User");
p.test2 = 3;
p.test3 = 4;
p.test4 = 5;

printf("Sending to Kernel:\n"); 
printf("index %d\n", p.index);
printf("dataSize %d\n", p.dataSize);
printf("data: %s\n", p.data);
printf("test2 %d\n", p.test2);
printf("test3 %d\n", p.test3);
printf("test4 %d\n", p.test4);

memcpy(NLMSG_DATA(nlh), (void *)&p, sizeof(Response));

iov.iov_base = (void *)nlh;
iov.iov_len = NLMSG_SPACE(MAX_PAYLOAD);
msg.msg_name = (void *)&dest_addr;
msg.msg_namelen = sizeof(dest_addr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;

printf("Sending message to kernel\n");
sendmsg(sock_fd,&msg,0);
printf("Waiting for message from kernel\n");

/* Read message from kernel */
recvmsg(sock_fd, &msg, 0);
req = (Response*)NLMSG_DATA(nlh);

printf("Recieved from Kernel:\n"); 
printf("index %d\n", req->index);
printf("dataSize %d\n", req->dataSize);
printf("data: %s\n", req->data);
printf("test2 %d\n", req->test2);
printf("test3 %d\n", req->test3);
printf("test4 %d\n", req->test4);

close(sock_fd);
}

全局.h

#ifndef __GLOBAL_H
#define __GLOBAL_H

typedef struct _Response Response;

struct _Response
{
  int index;
  int dataSize;     /* ammount of data in bytes of the response */
  char data[4096];

  int test2;
  int test3;
  int test4;
};

#endif

如果由于某种原因这对您不起作用,请告诉我。

关于c - 通过通用网络链接从内核到用户空间接收具有 2 个以上字段的结构时出现问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26440963/

相关文章:

java - 通过取消设置最右边的位来计算奇偶校验

c++ - IDCT矩阵的无分支生成?

c - PCI设备驱动导出信息到/proc文件系统

python - 在Python程序中使用Linux环境变量

linux - mount -t cifs 适用于一个版本的 linux 而不是另一个

c - 使用 CMake 在 CLion 中构建引发错误

linux - 如何在 KVM guest 中启用 PMU

php - 我的 codeigniter 无法在 centos 上运行(内部服务器错误 500)

c - 为什么在 Linux 字符驱动读取调用中 size 总是 = 4096?

multithreading - Linux 内核模块中 printk 的奇怪行为