Saltar al contenido

¿Cómo usar netlink socket para comunicarse con un módulo del kernel?

Luego de de una larga recopilación de datos pudimos resolver esta preocupación que pueden tener muchos de nuestros lectores. Te regalamos la solución y nuestro objetivo es que te resulte de gran apoyo.

Solución:

Después de leer la fuente del kernel, finalmente logré hacer que los sockets de netlink funcionen para mí. A continuación se muestra un ejemplo de los conceptos básicos del socket Netlink, es decir, abrir un socket netlink, leerlo y escribirlo y cerrarlo.

Módulo de kernel

#include 
#include  
#include 
#include  
#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: %sn", __FUNCTION__);

    msg_size = strlen(msg);

    nlh = (struct nlmsghdr *)skb->data;
    printk(KERN_INFO "Netlink received msg payload:%sn", (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 skbn");
        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 usern");


static int __init hello_init(void)


    printk("Entering: %sn", __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 modulen");
    netlink_kernel_release(nl_sk);


module_init(hello_init); module_exit(hello_exit);

MODULE_LICENSE("GPL");

Programa de usuario

#include 
#include 
#include 
#include 
#include 
#include 

#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 kerneln");
    sendmsg(sock_fd, &msg, 0);
    printf("Waiting for message from kerneln");

    /* Read message from kernel */
    recvmsg(sock_fd, &msg, 0);
    printf("Received message payload: %sn", NLMSG_DATA(nlh));
    close(sock_fd);

Hilo relacionado sobre la constante mágica. NETLINK_USER 31: ¿Puedo tener más de 32 sockets netlink en kernelspace?

En caso de que alguien no sepa cómo compilar, busque en Google "cómo compilar y cargar el módulo del kernel"

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

Tome el código fuente del kernel en el que compilará el módulo contra http://kernel.org

O simplemente actualice sus encabezados si está ejecutando el kernel previsto

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

Cree un archivo MAKE, por ejemplo

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

Haga y obtendrá un montón de archivos. * .ko es el que cargará en su kernel, ejecute

# insmod hello.ko

si usas lsmod para verificar todos los módulos cargados, encontrarás el tuyo, lo más probable es que veas:

hello       12575  0 

En nuestro caso, compile y ejecute el código de usuario:

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

Si todo está bien, recibirá el siguiente mensaje usando el código de binW:

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

Finalmente, elimine el módulo usando:

# rmmod hello

Me funciona con el kernel 3.2. Para el kernel 3.6 y superior, necesita un pequeño cambio en el netlink_kernel_create función.

 struct netlink_kernel_cfg cfg = 
                .groups = 1,
                .input = hello_nl_recv_msg,
        ;
        printk("Entering: %sn", __FUNCTION__);
        nl_sk = netlink_kernel_create(&init_net, NETLINK_USER, &cfg);

Recuerda que puedes dar visibilidad a esta noticia si te fue útil.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *