2017-01-05 12 views
0

В настоящее время я работаю над модулем ядра linux для школьного проекта, который включает в себя представление реализации хэш-хэшей ядра для пользовательского пространства.поведение msghdr с использованием Netlink для связи между пространством ядра и пространством пользователя

Для этого я создал API-интерфейс пользовательского пространства, который взаимодействует с LKM через сокет Netlink.

У меня сейчас есть работа, но я столкнулся с ударом, который оставил меня в замешательстве, и я действительно не могу обмотать голову. И после прочтения всей документации, которая действительно не помогла мне понять проблему и «спустилась по кроличьей дыре» и просмотрев исходный код для Netlink, я подумал, что задаю здесь вопрос, чтобы узнать, может ли кто-нибудь знать, что и почему, это происходит.

Таким образом, чтобы изолировать проблему, я создал небольшую тестовую программу, которая запускает общий пример использования пользовательского пространства Netlink и ядра. Благодаря этому я покажу 3 небольшие вариации программы пользовательского пространства, которые все имеют другое поведение, и это то, о чем я задаюсь.

Так первый является модуль ядра, который будет одинаковым для всех 3-х вариантах:

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

static int __init hello_init(void){ 

    struct netlink_kernel_cfg cfg = { 
     .input = hello_nl_recv_msg, 
    }; 
    printk(KERN_INFO "Loading kernel module\n"); 
    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 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"); 

И тогда программа пользовательского пространства:

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

#define NETLINK_USER 31 

#define MAX_PAYLOAD 1024 /* maximum payload size*/ 

struct msghdr msg; 

int main(){ 
    struct sockaddr_nl src_addr, dest_addr; 
    struct nlmsghdr *nlh = NULL; 
    struct iovec iov; 
    int sock_fd; 
    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_SPACE(MAX_PAYLOAD); 
    nlh->nlmsg_pid = getpid(); 
    nlh->nlmsg_flags = 0; 

    strcpy(NLMSG_DATA(nlh), "Hello"); 

    iov.iov_base = (void *)nlh; 
    iov.iov_len = nlh->nlmsg_len; 
    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); 
    printf("Received message payload: %s\n", (char*)NLMSG_DATA(nlh)); 
    close(sock_fd); 
    return 0; 
} 

Теперь, если я запустить это, все хорошо и хорошо, и это дает мне консольный выход:

Sending message to kernel 
Waiting for message from kernel 
Received message payload: Hello from kernel 

и выход журнала ядра из dmesg:

[ 3160.679609] exiting hello module 
[ 3165.140816] Loading kernel module 
[ 3169.678258] Entering: hello_nl_recv_msg 
[ 3169.678260] Netlink received msg payload:Hello 

Но для этого проекта мы используем многопоточные приложения, призывающие к API, поэтому я хотел бы попробовать и дать каждую вызывающей нить собственного NETLINK гнезда. Для этого мне пришлось сделать

struct msghdr msg; 

в локально объявленную переменную.

Проблемы возникают

Когда я переместил его в основную функцию вещи мгновенно сломал. Это так, что ядро ​​даже не вводит функцию обратного вызова Netlink, поэтому я думаю, что программа пространства пользователя не может даже записать ее, но она все равно возвращает правильное количество записанных байтов из функции sendmsg().

Это то, что выводится на консоль при наличии в msghdr локально объявленный:

Sending message to kernel 
Waiting for message from kernel 

А потом он висит, и должно быть SIGINT'ed, и журнал ядра ничего о LKM приема не показывать любые данные.

Итак, я начал задаваться вопросом, может ли быть ошибка адресации, когда она была объявлена ​​локально, поэтому, чтобы попробовать, я преобразовал msghdr в динамически выделенный указатель в локальной области, и вы знаете, что это сработало! В качестве исходного примера он выдал ту же консоль и выход журнала ядра.

Soooo, мой фактический вопрос действительно один для образовательной цели и понять, почему он ведет себя таким образом.

Почему глобально объявленная переменная работает, если локально объявленная не работает?

И, кроме того, почему локально объявленный динамически выделенный указатель работает?

Я пропустил что-то на фундаментальном уровне здесь?

TL; DR:

Почему это не работает, чтобы иметь msghdr-структуру локально объявленный в пространстве программы пользователя, в то время как во всем мире объявлен или локальный динамический указатель делает?

ответ

2

Возможно, когда он находится в стеке, память для него не обнуляется, и у вас есть мусор в каком-то поле.

+0

Это то, о чем я думал, но не могу найти хороший способ проверить его. Какие-либо предложения? –

+0

Попробуйте 'memset (& msg, 0, sizeof (msg));' перед установкой любых полей. – Velkan

+0

Конечно, я должен был это сделать .. Я глуп. Ха-ха. Я попробую и дам вам знать, как все прошло. –