Wie verwendet man den Netlink-Socket, um mit einem Kernel-Modul zu kommunizieren?

Lesezeit: 9 Minuten

Benutzeravatar von binW
binW

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 das nicht ändern kann, um es hinzuzufügen ioctl() oder irgendwas.

Das Problem ist, dass ich nicht herausfinden kann, wie das geht. Ich habe gegoogelt, aber alle Beispiele, die ich gefunden habe, sind für alt wie Dieses hier und für aktuelle Kernel-Versionen nicht mehr gültig. Ich habe mir diese SO-Frage auch angesehen, aber das Beispiel hier verwendet libnl für Socket-Operationen, aber ich möchte mich an Standard-Socket-Funktionen halten (definiert durch sys/socket.h). Kann mich also bitte jemand hier zu einem Tutorial oder einer Anleitung oder etwas führen, 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 sehr einfaches Beispiel dafür, 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ückerhält.

Sagen Sie mir bitte auch nicht, ich solle mir den Kernel-Code ansehen. Ich mache es bereits, aber es wird viel Zeit in Anspruch nehmen und ich habe nicht mehr viel davon übrig.

Aktualisieren:
Nach viel Versuch und Irrtum habe ich folgenden Code, der eine Nachricht vom Benutzerprogramm an den Kernel sendet, aber die Nachricht vom Kernel an das Benutzerprogramm, dh 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 einen Blick darauf werfen und mir sagen, was ich falsch mache. Das netlink_unicast() Aufruf wird im folgenden Code kommentiert. Es sollte für Kernel-zu-Benutzerprogramm-Meldungen unkommentiert 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);  

  • Wie können 10 Personen dies als Favorit markieren, aber nur 5 Personen stimmen dafür?

    – Donal Lafferty

    4. September 2013 um 15:10 Uhr

  • Ich weiß, es ist ein bisschen spät, aber dieses Buch (Kap. 2) behandelt auch Netlink-Sockets: amazon.com/Linux-Kernel-Networking-Implementation-Experts/dp/…

    – holgac

    1. April 2015 um 14:38 Uhr

  • dieses Anwenderprogramm arbeitet solange struct msghdr msg; ist im globalen Geltungsbereich definiert. Aber sobald ich das in eine Funktion (z. B. main) verschiebe, funktioniert das Benutzerprogramm nicht mehr und sendmsg gibt -1 zurück und errno wird auf Fehler 105 gesetzt (ENOBUFS – kein Pufferplatz verfügbar). Kann jemand erklären warum msghdr funktioniert nur, wenn global in diesem Programm definiert?

    – Ungeklebt

    12. März 2019 um 23:26 Uhr

Benutzeravatar von binW
binW

Nachdem ich die Kernelquellen gelesen hatte, gelang es mir endlich, Netlink-Sockets für mich zum Laufen zu bringen. Nachfolgend finden Sie ein Beispiel für die Grundlagen von Netlink-Sockets, dh Öffnen eines Netlink-Sockets, Lesen und Schreiben darauf 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?

  • Können Sie auch bitte die README-Datei hinzufügen, damit andere dies erstellen/ausführen und testen und Feedback geben können. Danke

    – Raulp

    23. Mai 2012 um 17:58 Uhr

  • Wie kompilieren? Gibt es eine Option zum Kompilieren?

    – duslabo

    11. November 2012 um 4:51 Uhr

  • Ist es möglich mit user=31 zu laufen? Ich konnte es nur mit user = 0 zum Laufen bringen.

    – lang2

    22. März 2014 um 14:12 Uhr

  • Ist es möglich, netlink_kernel_create() innerhalb einer Funktion anstelle der Kernel-Init auszuführen? Also, dass diese Funktion ein Listener für Userspace-Nachrichten ist?

    – Benutzer1252280

    7. Oktober 2014 um 22:21 Uhr

  • Wollen Sie skb_out nicht freigeben? Ich habe gerade ein ähnliches Kernel-Modul geschrieben, und es stürzt ab, wenn ich kfree_skb(skb_out) versuche.

    – Abhishek Sagar

    9. Juli 2019 um 19:17 Uhr


Nur für den Fall, dass jemand nicht weiß, wie man kompiliert, google “How to compile and load kernel module”

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

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

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

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

Erstellen Sie beispielsweise 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 Menge Dateien. *.ko ist diejenige, die Sie in Ihren Kernel laden, ausführen

# insmod hello.ko

Wenn Sie lsmod verwenden, um alle geladenen Module zu überprüfen, werden Sie Ihre finden, höchstwahrscheinlich werden Sie Folgendes sehen:

hello       12575  0 

Kompilieren Sie in unserem Fall den Benutzercode und führen Sie ihn aus:

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

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

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

Entfernen Sie schließlich das Modul mit:

# rmmod hello

Benutzeravatar von yvo.engr
yvo.engr

Bei mir funktioniert es mit Kernel 3.2. Für Kernel 3.6 und höher ist eine kleine Änderung am erforderlich netlink_kernel_create Funktion.

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

Sie müssen die folgende Header-Datei in den clientseitigen Code einfügen:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

1409480cookie-checkWie verwendet man den Netlink-Socket, um mit einem Kernel-Modul zu kommunizieren?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy