wake-up-neo.com

Wie verwende ich netlink socket, um mit einem Kernelmodul zu kommunizieren?

Ich versuche, ein Linux-Kernel-Modul zu schreiben, das über netlink mit dem Benutzerprozess kommuniziert. Ich verwende netlink, weil das Benutzerprogramm, mit dem ich kommunizieren möchte, nur über Sockets kommuniziert, und ich kann es nicht ändern, um ioctl() oder irgendetwas hinzuzufügen.

Das Problem ist, dass ich nicht herausfinden kann, wie das geht. Ich habe gegoogelt, aber alle Beispiele, die ich gefunden habe, sind für alte wie dieses und nicht mehr für aktuelle Kernel-Versionen gültig. Ich habe mir auch this SO - Frage angesehen aber das Beispiel hier verwendet libnl für Socket-Operationen, aber ich möchte an Standard-Socket-Funktionen (definiert durch sys/socket.h) festhalten. Kann mir jemand hier ein paar Anleitungen oder Anleitungen geben oder etwas, das mir helfen kann, die Benutzeroberfläche und die Verwendung von netlink zu verstehen. Ich würde ein funktionierendes Beispiel sehr schätzen, nichts Besonderes, nur ein einfaches Beispiel, wie man eine Verbindung von einem Socket im Benutzerprogramm zu einem Socket im Kernel herstellt und dann Daten vom Benutzerprozess an den Kernel sendet und vom Kernel zurücksendet.

Bitte sagen Sie mir auch nicht, den Kernel-Code anzusehen. Ich mache es bereits, aber es wird viel Zeit in Anspruch nehmen und ich habe nicht viel davon übrig.

Update: Nach vielen Versuchen und Fehlern habe ich folgenden Code, der eine Nachricht vom Benutzerprogramm an den Kernel sendet, die Nachricht vom Kernel an das Benutzerprogramm d. H. Mit netlink_unicast() funktioniert nicht. Es funktioniert nicht nur nicht, der Anruf hängt die Systeme und dann muss ich die Maschine neu starten. Kann jemand bitte nachschauen und mir sagen, was ich falsch mache. Der Aufruf netlink_unicast() wird im folgenden Code kommentiert. Es sollte unkommentiert für Kernel-Benutzer-Programmnachricht sein.

Benutzerprogramm

#include <sys/socket.h>  
#include <linux/netlink.h>  
#define NETLINK_USER 31  
#define MAX_PAYLOAD 1024  /* maximum payload size*/  

struct sockaddr_nl src_addr, dest_addr;  
struct nlmsghdr *nlh = NULL;  
struct iovec iov;  
int sock_fd;  
struct msghdr msg;  

void 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 */  
    /* interested in group 1<<0 */  
    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",  
        NLMSG_DATA(nlh));  
    close(sock_fd);  
}

Kernel-Code

#include <linux/module.h>  
#include <linux/kernel.h>  
#include <linux/init.h>  
#include <net/sock.h>  
#include <linux/socket.h>  
#include <linux/net.h>  
#include <asm/types.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;  

        printk(KERN_INFO "Entering: %s\n", __FUNCTION__);  

        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 */  
        NETLINK_CB(skb).dst_group = 0; /* not in mcast group */  
        NETLINK_CB(skb).pid = 0;      /* from kernel */  
        //NETLINK_CB(skb).groups = 0; /* not in mcast group */  
        //NETLINK_CB(skb).dst_pid = pid;  
        printk("About to send msg bak:\n");  
        //netlink_unicast(nl_sk,skb,pid,MSG_DONTWAIT);  

}  

static int __init hello_init(void)  
{  

        printk("Entering: %s\n",__FUNCTION__);  
        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);  
42
binW

Nachdem ich Kernel-Quellen gelesen hatte, gelang es mir endlich, Netlink-Sockets für mich zum Laufen zu bringen. Im Folgenden finden Sie ein Beispiel für Netlink-Sockel-Grundlagen, d. H. Einen Netlink-Sockel öffnen, lesen und schreiben und schließen.

Kernel-Modul

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

    printk("Entering: %s\n", __FUNCTION__);
    //nl_sk = netlink_kernel_create(&init_net, NETLINK_USER, 0, hello_nl_recv_msg, NULL, THIS_MODULE);
    struct netlink_kernel_cfg cfg = {
        .input = hello_nl_recv_msg,
    };

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

Benutzerprogramm

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

#define NETLINK_USER 31

#define MAX_PAYLOAD 1024 /* maximum payload size*/
struct sockaddr_nl src_addr, dest_addr;
struct nlmsghdr *nlh = NULL;
struct iovec iov;
int sock_fd;
struct msghdr msg;

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));
    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", NLMSG_DATA(nlh));
    close(sock_fd);
}

Verwandter Thread über die magische Konstante NETLINK_USER 31: Kann ich mehr als 32 netlink-Sockets im Kernelspace haben?

51
binW

Nur für den Fall, dass jemand nicht wissen kann, wie er kompiliert wird, google "das Kernelmodul kompilieren und laden"

siehe http://www.cyberciti.biz/tips/compiling-linux-kernel-module.html

Besorgen Sie sich den Kernel-Quellcode, in den Sie das Modul unter http://kernel.org kompilieren.

Oder aktualisieren Sie einfach Ihre Header, wenn Sie den beabsichtigten Kernel ausführen

# apt-get install kernel-headers-$(uname -r)

Erstellen Sie zum Beispiel ein Makefile

obj-m = hello.o
KVERSION = $(Shell uname -r)
all:
        make -C /lib/modules/$(KVERSION)/build M=$(PWD) modules
clean:
        make -C /lib/modules/$(KVERSION)/build M=$(PWD) clean

Machen Sie und Sie erhalten eine Reihe von Dateien. * .ko ist die, die Sie in Ihren Kernel laden werden

# insmod hello.ko

wenn Sie lsmod verwenden, um alle geladenen Module zu überprüfen, werden Sie Ihre finden. Wahrscheinlich werden Sie Folgendes sehen:

hello       12575  0 

In unserem Fall den Benutzercode kompilieren und ausführen:

gcc hello.c -o hello.o
./hello.o

Wenn alles in Ordnung ist, erhalten Sie folgende Nachricht mit dem Code von binW:

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

Zum Schluss entfernen Sie das Modul mit:

# rmmod hello
8
guoger

Es funktioniert für mich mit Kernel 3.2. Für Kernel 3.6 und höher muss die Funktion netlink_kernel_create etwas geändert werden.

 struct netlink_kernel_cfg cfg = {
                .groups = 1,
                .input = hello_nl_recv_msg,
        };
        printk("Entering: %s\n", __FUNCTION__);
        nl_sk = netlink_kernel_create(&init_net, NETLINK_USER, &cfg);
5
yvo.engr

sie müssen die folgende Header-Datei in den client_side-Code einfügen:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
0
Mostafa Dsg