Как отправлять и получать сообщения от функции, отличной от зарегистрированной функции обратного вызова в сокете Netlink?

В следующем модуле ядра я перехватил системный вызов sys_open, и теперь, пытаясь отправить имя файла в процесс в пользовательском пространстве с помощью сокета Netlink, в ответ процесс вернет сообщение, а затем, в соответствии с сообщением, модуль ядра продолжит работу.

исходный код: foo.c

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

Функция «hello_nl_recv_msg», которая является функцией обратного вызова, отправляет и получает сообщения процессу, но Как я могу отправить сообщение (т. е. имя файла) из функции «my_sys_open» для обработки в пользовательском пространстве? и как дождаться ответа?

Makefile:

obj-m += foo.o

all:        
    make -C /usr/src/linux-headers-3.2.0-23-generic/ M=$(PWD) modules

clean: 
    make -C /usr/src/linux-headers-3.2.0-23-generic/ M=$(PWD) clean

Спасибо за ваше время ;)


person Nitinkumar Ambekar    schedule 27.07.2015    source источник
comment
При необходимости найдите исходный код пользовательского пространства для сокета Netlink здесь: stackoverflow.com/a/3334782/2706918   -  person Nitinkumar Ambekar    schedule 27.07.2015


Ответы (1)


  1. Как я могу отправить сообщение (т. е. имя файла) из функции «my_sys_open» для обработки в пользовательском пространстве?

Пользовательская программа должна создать сокет AF_NETLINK, адрес этого сокета будет использоваться для отправки сообщения на него. Подробную информацию читайте man netlink.

  1. и как дождаться ответа?

Вы можете использовать любой стандартный механизм для создания события ожидания ответа my_sys_open в hello_nl_recv_msg, например wait_event. Упрощенный код:

/* 
 * 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
    ....
}
person Tsyvarev    schedule 27.07.2015
comment
Не могли бы вы предложить код для функции my_sys_open для отправки/получения сообщений? - person Nitinkumar Ambekar; 27.07.2015
comment
Я добавил код, который реализует ожидание ответа. - person Tsyvarev; 27.07.2015
comment
Спасибо @Tsyvarev за ответ. Я понял, чтобы избежать обработки полученного сообщения в функции обратного вызова, мы можем использовать функцию wake_up_### (в обратном вызове) и ###_unicast (в обработке) в соответствии с доступность в конкретной версии ядра. Как сделать то же самое в версии ядра 3.2.0-23-generic? Как я могу узнать, какие функции доступны для этого в разных версиях ядра? - person Nitinkumar Ambekar; 31.07.2015
comment
Просто проверьте исходники ядра, с которым вы работаете. И объявления, и определения функций редко перемещаются между версиями, поэтому область поиска невелика. Кроме того, иногда полезно использовать поиск или поиск с помощью git. - person Tsyvarev; 31.07.2015
comment
Нужно ли получать первое сообщение из программы пользовательского пространства? Разве мы не можем создать сокет в пользовательском пространстве и начать связь из пространства ядра? Так как нам нужно использовать PID в nlmsg_unicast в пространстве ядра. - person Nitinkumar Ambekar; 16.09.2015
comment
Вы можете инициировать связь из пространства ядра. Обратите внимание, что pid здесь не идентификатор процесса, а идентификатор порта. Поэтому вам нужно знать порт сокета пользовательского пространства. - person Tsyvarev; 16.09.2015