我正在尝试基于此示例http://www.electronicsfaq.com/2014/02/generic-netlink-sockets-example-code.html将LKM中的结构发送/接收到用户空间(反之亦然)。
我将结构发送到内核。它接收结构ok,它处理信息,并将结构发送回userland。但是,如果结构有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(这是必要的)。
答案 0 :(得分:0)
内核部分中的这一行是主要问题。指针的大小为8(在64位机器上)或4.您只发送前几个字节。
rc = nla_put(skb, DOC_EXMPL_A_MSG2, sizeof(req),req);
它应该是&#34; sizeof(* req)&#34;。我试了一下,但是仍有一个数据被几个字节偏移的问题......我猜测其中一些宏并没有做你认为他们正在做的事情。
下面是我之前做过的一个例子,我将其改为使用像你这样的结构,它按预期工作。在我看来,这段代码更加清晰。
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);
}
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];
int test2;
int test3;
int test4;
};
#endif
如果由于某种原因这对您不起作用,请告诉我。