[Linux-kernel] Comment utiliser socket netlink pour communiquer avec un module noyau?


Answers

Juste au cas où quelqu'un ne sait pas comment compiler, google "comment compiler et charger le module du noyau"

se référer à http://www.cyberciti.biz/tips/compiling-linux-kernel-module.html

Saisissez le code source du noyau sur lequel vous compilerez le module contre http://kernel.org

Ou simplement mettre à jour vos en-têtes si vous exécutez le noyau prévu

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

Créer un makefile, par exemple

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

Faites et vous aurez beaucoup de fichiers. * .ko est celui que vous chargez dans votre noyau, lancez

# insmod hello.ko

Si vous nous demandez de vérifier tous les modules chargés, vous trouverez le vôtre, vous verrez probablement:

hello       12575  0 

Dans notre cas, compilez et exécutez le code utilisateur:

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

Si tout est OK, vous recevrez le message suivant en utilisant le code de binW:

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

Enfin, retirez le module en utilisant:

# rmmod hello
Question

J'essaye d'écrire un module de noyau de Linux qui communique avec le processus d'utilisateur en utilisant netlink. J'utilise netlink car le programme utilisateur que je veux communiquer ne communique qu'avec des sockets et je ne peux pas changer cela pour ajouter ioctl() ou quoi que ce soit.

Le problème est que je ne peux pas comprendre comment faire cela. J'ai googlé mais tous les exemples que j'ai trouvés sont anciens comme celui-ci et ne sont plus valables pour les versions actuelles du noyau. J'ai aussi regardé cette question SO mais l'exemple ici utilise libnl pour les opérations de socket mais je veux m'en tenir aux fonctions de socket standard (définies par sys/socket.h ). Donc, quelqu'un peut me guider ici pour un tutoriel ou un guide ou quelque chose qui peut m'aider à comprendre l'interface et l'utilisation de netlink. J'apprécierais hautement un exemple de travail, rien d'extraordinaire, juste un exemple très simple de comment établir une connexion depuis un socket dans un programme utilisateur vers une socket dans le noyau puis envoyer des données du processus utilisateur au noyau et recevoir du noyau.

Aussi s'il vous plaît ne me dites pas de regarder le code du noyau. Je le fais déjà mais cela prendra beaucoup de temps et je n'en ai plus beaucoup.

Mise à jour: Après beaucoup d'essais et d'erreurs, j'ai le code suivant qui envoie un message du programme utilisateur au noyau mais le message du noyau au programme utilisateur, c'est-à-dire en utilisant netlink_unicast() ne fonctionne pas. Non seulement cela ne fonctionne pas, mais l'appel bloque les systèmes et je dois redémarrer la machine. Est-ce que quelqu'un peut jeter un coup d'oeil et me dire ce que je fais mal. L'appel netlink_unicast() est commenté dans le code suivant. Il ne doit pas être commenté pour le noyau du message du programme utilisateur.

Programme utilisateur

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

Code du noyau

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



vous devez inclure le fichier d'en-tête suivant dans le code client:

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