web-dev-qa-db-fra.com

Comment utiliser le socket netlink pour communiquer avec un module de noyau?

J'essaie d'écrire un module de noyau Linux qui communique avec le processus utilisateur à l'aide de Netlink. J'utilise netlink parce que le programme utilisateur avec lequel je veux communiquer communique uniquement à l'aide de 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 pour des anciens comme celui-ci et ne sont plus valides pour les versions actuelles du noyau. J'ai également examiné cette SO question , mais l'exemple utilisé utilise libnl pour les opérations de socket, mais je souhaite m'en tenir aux fonctions de socket standard (définies par sys/socket.h). Alors, est-ce que quelqu'un peut me guider ici vers un tutoriel ou un guide ou quelque chose qui peut m'aider à comprendre l'interface et l'utilisation de netlink. J'apprécierais beaucoup un exemple de travail, rien d'extraordinaire, juste un exemple très basique sur la façon d'établir une connexion depuis un socket du programme utilisateur vers un socket dans le noyau, puis d'envoyer des données du processus utilisateur au noyau et de les 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 i.e utilisant netlink_unicast() ne fonctionne pas. Non seulement cela ne fonctionne pas, l'appel bloque les systèmes et je dois ensuite redémarrer la machine. Quelqu'un peut-il jeter un coup d'oeil et me dire ce que je fais de mal. L'appel netlink_unicast() est commenté dans le code suivant. Il devrait être supprimé pour le message du programme noyau à 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);  
42
binW

Après avoir lu la source du noyau, j'ai finalement réussi à faire fonctionner les sockets netlink pour moi. Vous trouverez ci-dessous un exemple des bases du support Netlink, à savoir l’ouverture, la lecture et l’écriture d’un socket Netlink et sa fermeture.

Module de noyau

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

Programme utilisateur

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

Sujet connexe relatif à la constante magique NETLINK_USER 31: Puis-je avoir plus de 32 sockets netlink dans le noyau?

51
binW

Juste au cas où quelqu'un ne sache 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 dans lequel vous compilerez le module/ http://kernel.org

Ou simplement mettre à jour vos en-têtes si vous utilisez 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 un tas de fichiers. * .ko est celui que vous allez charger dans votre noyau, lancez

# insmod hello.ko

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

hello       12575  0 

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

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

Si tout va bien, 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, supprimez le module en utilisant:

# rmmod hello
8
guoger

Cela fonctionne pour moi avec le noyau 3.2. Pour les noyaux 3.6 et supérieurs, il faut un peu changer la fonction netlink_kernel_create.

 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

vous devez inclure le fichier d’en-tête suivant dans le code côté client:

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