2017-01-05 19 views
0

현재 사용자 공간에 커널 해시 테이블 구현을 노출시키는 학교 프로젝트 용 Linux 커널 모듈을 만들고 있습니다.커널 공간과 사용자 공간 사이의 통신을 위해 Netlink를 사용하는 msghdr 동작

이것을 달성하기 위해 Netlink 소켓을 통해 LKM과 통신하는 사용자 공간 API도 만들었습니다.

나는 지금 일하고있다. 그러나 나는 나에게 상당히 혼란하게했던 충돌에 부딪쳤다. 그리고 나는 정말로 나의 머리를 감쌀 수 없다. 그리고 실제로 모든 문제를 이해하는데 도움이되지 않은 모든 문서를 읽고 "토끼의 구멍으로 내려 가서"Netlink의 소스 코드를 조사한 결과 나는 질문을 던져서 누군가가 무엇이, 왜 그런지 알고 있습니다.

그래서 문제를 분리하기 위해 일반 Netlink 사용자 공간 및 커널 공간 통신 예제를 실행하는 작은 테스트 프로그램을 만들었습니다. 이를 통해 사용자 공간 프로그램의 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; 
} 

내가 만약

그래서 먼저 3 개 유사 위해 동일을 할 것이다 커널 모듈이며, 이걸 실행하면 모두 잘되고 잘 콘솔 출력을 내게 준다 :

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() 함수에서 올바른 양의 쓰기 바이트를 반환합니다.

Sending message to kernel 
Waiting for message from kernel 

그리고 그것을 중단하고 SIGINT'ed해야하고, 커널 로그가 LKM의 수신에 대해 아무것도 표시되지 않습니다

이 로컬 선언는 msghdr을 가진 경우 콘솔로 출력이 무엇이다 모든 데이터.

로컬로 선언 할 때 주소 지정 오류가 발생할 수 있는지 궁금해하기 시작 했으므로 msghdr을 로컬 범위에서 동적으로 할당 된 포인터로 변환했습니다. 원래 예제와 동일한 콘솔 및 커널 로그 출력을 제공합니다.

Soooo 나의 실제적인 질문은 실제로 교육적인 목적을위한 것이고 이것이 왜 이런 식으로 행동하는지 이해하는 것입니다.

왜 전역 적으로 선언 된 변수가 로컬로 선언 된 변수에서 작동하지 않습니까?

또한 로컬에서 선언되고 동적으로 할당 된 포인터가 작동하는 이유는 무엇입니까?

나는 근본적인 수준에서 뭔가를 놓치고 있습니까?

TL; DR :

이 왜 전 세계적으로 선언 또는 로컬 동적 포인터가하는 동안, 로컬 사용자 공간 프로그램에 선언는 msghdr 구조체를 가지고 작동하지 않는 이유는 무엇입니까?

답변

2

어쩌면 스택에있을 때 메모리가 0이 아니며 일부 필드에 가비지가 있습니다.

+0

그건 제가 생각한 것입니다. 그러나 실제로 테스트 할 좋은 방법을 찾을 수는 없습니다. 어떤 제안? –

+0

필드를 설정하기 전에'memset (& msg, 0, sizeof (msg));를 시도하십시오. – Velkan

+0

Ofcourse, 나는 그것을 알았어야했다.. 나는 어리 석다.. 하하. 나는 그것을 시험해보고 그것이 어떻게 진행되었는지 알려 줄 것이다. –