C 如何从Netlink套接字中注册的回调函数以外的函数发送和接收消息?
在下面的内核模块中,我钩住了syscall sys_open,现在尝试使用Netlink套接字将文件名发送到用户空间中的进程,作为响应,进程将返回一个msg,然后根据msg,内核模块将继续进行 源代码:foo.cC 如何从Netlink套接字中注册的回调函数以外的函数发送和接收消息?,c,sockets,linux-kernel,kernel-module,netlink,C,Sockets,Linux Kernel,Kernel Module,Netlink,在下面的内核模块中,我钩住了syscall sys_open,现在尝试使用Netlink套接字将文件名发送到用户空间中的进程,作为响应,进程将返回一个msg,然后根据msg,内核模块将继续进行 源代码:foo.c #include <linux/module.h> #include <linux/init.h> #include <linux/types.h> #include <asm/uaccess.h> #include <asm/ca
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <asm/uaccess.h>
#include <asm/cacheflush.h>
#include <linux/syscalls.h>
#include <linux/delay.h> // loops_per_jiffy
//===============netlink=================
#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;
//===============netlink=================
#define CR0_WP 0x00010000 // Write Protect Bit (CR0:16)
/* Just so we do not taint the kernel */
MODULE_LICENSE("GPL");
void **syscall_table;
unsigned long **find_sys_call_table(void);
long (*orig_sys_open)(const char __user *filename, int flags, int mode);
//===============netlink=================
static void hello_nl_recv_msg(struct sk_buff *skb)
{
struct nlmsghdr *nlh;
int pid;
struct sk_buff *skb_out;
int msg_size;
char *msg = "Hello from kernel";
int res;
printk(KERN_INFO "Entering: %s\n", __FUNCTION__);
msg_size = strlen(msg);
nlh = (struct nlmsghdr *)skb->data;
printk(KERN_INFO "Netlink received msg payload: %s\n", (char *)nlmsg_data(nlh));
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 */
strncpy(nlmsg_data(nlh), msg, msg_size);
res = nlmsg_unicast(nl_sk, skb_out, pid);
if (res < 0)
printk(KERN_INFO "Error while sending bak to user\n");
}
//===============netlink=================
unsigned long **find_sys_call_table()
{
unsigned long ptr;
unsigned long *p;
for (ptr = (unsigned long)sys_close;
ptr < (unsigned long)&loops_per_jiffy;
ptr += sizeof(void *))
{
p = (unsigned long *)ptr;
if (p[__NR_close] == (unsigned long)sys_close)
{
printk(KERN_DEBUG "Found the sys_call_table!!!\n");
return (unsigned long **)p;
}
}
return NULL;
}
long my_sys_open(const char __user *filename, int flags, int mode)
{
long ret;
//Send filename & get response from user space app
if(/*user_space_response ==*/ 0)
{
/*Other processing*/
}
ret = orig_sys_open(filename, flags, mode);
printk(KERN_DEBUG "file %s has been opened with mode %d\n", filename, mode);
return ret;
}
static int __init syscall_init(void)
{
int ret;
unsigned long addr;
unsigned long cr0;
syscall_table = (void **)find_sys_call_table();
if (!syscall_table)
{
printk(KERN_DEBUG "Cannot find the system call address\n");
return -1;
}
//===============netlink=================
nl_sk = netlink_kernel_create(&init_net, NETLINK_USER, 0, hello_nl_recv_msg, NULL, THIS_MODULE);
if (!nl_sk)
{
printk(KERN_DEBUG "Error creating socket.\n");
return -1;
}
//===============netlink=================
cr0 = read_cr0();
write_cr0(cr0 & ~CR0_WP);
addr = (unsigned long)syscall_table;
ret = set_memory_rw(PAGE_ALIGN(addr) - PAGE_SIZE, 3);
if(ret)
{
printk(KERN_DEBUG "Cannot set the memory to rw (%d) at addr %16lX\n", ret, PAGE_ALIGN(addr) - PAGE_SIZE);
}
else
{
printk(KERN_DEBUG "3 pages set to rw");
}
orig_sys_open = syscall_table[__NR_open];
syscall_table[__NR_open] = my_sys_open;
write_cr0(cr0);
return 0;
}
static void __exit syscall_release(void)
{
unsigned long cr0;
cr0 = read_cr0();
write_cr0(cr0 & ~CR0_WP);
syscall_table[__NR_open] = orig_sys_open;
write_cr0(cr0);
netlink_kernel_release(nl_sk);
}
module_init(syscall_init);
module_exit(syscall_release);
谢谢你抽出时间;)
AF_NETLINK
,此套接字的地址将用于向其发送消息。有关详细信息,请阅读mannetlink
hello\u nl\u recv\u msg
中使用任何标准机制来打开my\u sys\u
等待响应事件,例如。简化代码:
/*
* Whether responce is recieved.
*
* For process concurrent open's this should be map,
* e.g., struct task_struct -> bool.
*/
int have_responce = 0;
DECLARE_WAIT_QUEUE_HEAD(responce_waitqueue); // Waitqueue for wait responce.
static void hello_nl_recv_msg(struct sk_buff *skb)
{
...
if(<detect responce from user program>)
{
have_responce = 1;
wake_up_all(responce_waitqueue);
}
...
}
long my_sys_open(const char __user *filename, int flags, int mode)
{
struct sk_buff *skb_out;
...
have_responce = 0; // clear responce flag
nlmsg_unicast(nl_sk, skb_out, <stored_user_pid>);// send message
wait_event(responce_waitqueue, have_responce); //wait until responce is received
....
}
/*
*是否收到响应。
*
*对于进程并发打开,这应该是映射,
*例如,结构任务\u结构->布尔。
*/
int有_response=0;
声明等待队列头(响应等待队列);//等待队列等待响应。
静态无效hello_nl_recv_msg(结构sk_buff*skb)
{
...
if()
{
有_response=1;
唤醒所有人(响应等待队列);
}
...
}
长时间my_sys_打开(常量字符用户*文件名、整型标志、整型模式)
{
结构sk_buff*skb_out;
...
have_response=0;//清除response标志
nlmsg_单播(nl_sk,skb_out,);//发送消息
wait_event(response_waitqueue,have_response);//等待直到收到response
....
}
如果需要,请在此处查找Netlink套接字的用户空间源代码:请建议“my_sys_open”函数的代码以发送/接收消息?我添加了实现等待响应的代码。感谢@Tsyvarev的回答。我明白了,为了避免在回调函数中处理接收到的消息,我们可以根据特定内核版本的可用性使用wake_up_35;########单播(in processing)函数。如何在内核版本3.2.0-23-generic中执行相同的操作?我怎么知道在不同的内核版本中哪些函数可以实现这一点呢?只需查看您使用的内核的源代码。函数的声明和定义很少在不同的版本之间移动,所以搜索区域不大。此外,使用git进行grepping或搜索有时也很有用。您可以从内核空间启动通信。注意,pid
这里不是进程ID而是端口ID。所以您需要知道用户空间套接字的端口。
/*
* Whether responce is recieved.
*
* For process concurrent open's this should be map,
* e.g., struct task_struct -> bool.
*/
int have_responce = 0;
DECLARE_WAIT_QUEUE_HEAD(responce_waitqueue); // Waitqueue for wait responce.
static void hello_nl_recv_msg(struct sk_buff *skb)
{
...
if(<detect responce from user program>)
{
have_responce = 1;
wake_up_all(responce_waitqueue);
}
...
}
long my_sys_open(const char __user *filename, int flags, int mode)
{
struct sk_buff *skb_out;
...
have_responce = 0; // clear responce flag
nlmsg_unicast(nl_sk, skb_out, <stored_user_pid>);// send message
wait_event(responce_waitqueue, have_responce); //wait until responce is received
....
}