español linux-kernel linux-device-driver

linux-kernel - español - linux device drivers



¿Cómo usar netlink socket para comunicarse con un módulo kernel? (4)

Después de leer la fuente del kernel finalmente logré hacer que los sockets de netlink me funcionaran. A continuación se muestra un ejemplo de conceptos básicos de conexión de Netlink, es decir, abrir un socket de enlace de red, leer y escribir y cerrarlo.

Módulo Kernel

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

Programa de usuario

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

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

Estoy tratando de escribir un módulo de kernel de Linux que se comunique con el proceso de usuario usando netlink. Estoy usando netlink porque el programa de usuario que quiero comunicar solo se comunica usando sockets y no puedo cambiar eso para agregar ioctl() ni nada.

El problema es que no puedo descifrar cómo hacerlo. He buscado en Google, pero todos los ejemplos que encontré son antiguos como este y ya no son válidos para las versiones actuales del kernel. También he examinado esta pregunta SO, pero el ejemplo aquí usa libnl para las operaciones de socket, pero quiero libnl a las funciones de socket estándar (definidas por sys/socket.h ). Entonces, ¿puede alguien por favor guiarme aquí a algún tutorial o guía o algo que pueda ayudarme a entender la interfaz y el uso de netlink? Agradecería mucho un ejemplo de trabajo, nada sofisticado, solo un ejemplo muy básico de cómo establecer una conexión desde un socket en un programa de usuario a un socket en kernel y luego enviar datos desde el proceso de usuario a kernel y recibir de nuevo desde kernel.

También, por favor no me digas que mire el código del kernel. Ya lo estoy haciendo, pero llevará mucho tiempo y no me queda mucho.

Actualización: Después de mucho intento de prueba y error, tengo el siguiente código que envía mensajes desde el programa de usuario a kernel, pero el mensaje de kernel a programa de usuario, es decir, el uso de netlink_unicast() no funciona. No solo no funciona, la llamada cuelga los sistemas y luego tengo que reiniciar la máquina. ¿Puede alguien echar un vistazo y decirme qué mal estoy haciendo? La llamada netlink_unicast() se comenta en el siguiente código. Debe ser descomentada para kernel a mensaje de programa de usuario.

Programa de usuario

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

Código Kernel

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


En caso de que alguien no sepa cómo compilar, googlee "cómo compilar y cargar el módulo kernel"

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

Toma el código fuente del núcleo al que compilarás el módulo contra http://kernel.org

O simplemente actualice sus encabezados si está ejecutando kernel previsto

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

Crear 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á muchos archivos. * .ko es el que cargarás en tu núcleo, ejecuta

# insmod hello.ko

si usted nos lsmod comprueba todos los módulos cargados, encontrará los suyos, lo más probable es que vea:

hello 12575 0

En nuestro caso, compilar y ejecutar 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 binW:

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

Finalmente, elimine el módulo usando:

# rmmod hello


Funciona para mí con kernel 3.2. Para kernel 3.6 y superior, necesita un poco de cambio en la función 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);


necesita incluir el siguiente archivo de encabezado en el código client_side:

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