Estaba tratando de escribir un programa simple que se comunicara entre el kernel y el espacio del usuario usando Netlink. Básicamente esto es lo que quería lograr:
- El programa de espacio de usuario comienza a vincularse a un grupo de multidifusión definido por el usuario.
- Insertar módulo del núcleo
- El módulo del núcleo envía un mensaje a este grupo de multidifusión
- El programa de espacio de usuario recibe el mensaje.
Aquí está mi código:
======Programa de espacio de usuario======
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<linux/netlink.h>
#include<sys/types.h>
#include<unistd.h>
#define MYPROTO NETLINK_USERSOCK
#define MYMGRP 0x21 //User defined group, consistent in both kernel prog and user prog
int open_netlink()
{
int sock = socket(AF_NETLINK,SOCK_RAW,MYPROTO);
struct sockaddr_nl addr;
memset((void *)&addr, 0, sizeof(addr));
if (sock<0)
return sock;
addr.nl_family = AF_NETLINK;
addr.nl_pid = getpid();
addr.nl_groups = MYMGRP;
if (bind(sock,(struct sockaddr *)&addr,sizeof(addr))<0)
return -1;
return sock;
}
int read_event(int sock)
{
struct sockaddr_nl nladdr;
struct msghdr msg;
struct iovec iov[2];
struct nlmsghdr nlh;
char buffer[65536];
int ret;
iov[0].iov_base = (void *)&nlh;
iov[0].iov_len = sizeof(nlh);
iov[1].iov_base = (void *)buffer;
iov[1].iov_len = sizeof(buffer);
msg.msg_name = (void *)&(nladdr);
msg.msg_namelen = sizeof(nladdr);
msg.msg_iov = iov;
msg.msg_iovlen = sizeof(iov)/sizeof(iov[0]);
ret=recvmsg(sock, &msg, 0);
if (ret<0) {
return ret;
}
printf("Received message payload: %s\n", NLMSG_DATA(&nlh));
}
int main(int argc, char *argv[])
{
int nls = open_netlink();
if (nls<0) {
err(1,"netlink");
}
while (1)
read_event(nls);
return 0;
}
======Módulo del núcleo======
#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/rtnetlink.h>
#include <linux/skbuff.h>
#include <linux/delay.h>
#define NETLINK_USER 31
#define MYGRP 0x21 //User defined group, consistent in both kernel prog and user prog
struct sock *nl_sk = NULL;
static void send_to_user() {
struct sk_buff *skb_out;
struct nlmsghdr *nlh;
int msg_size;
char *msg = "Hello from kernel";
int res;
printk(KERN_INFO "Entering: %s\n", __FUNCTION__);
msg_size = strlen(msg);
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, 1, NLMSG_DONE, msg_size, 0);
//NETLINK_CB(skb_out).dst_group = 1; /* Multicast to group 1, 1<<0 */
strncpy(nlmsg_data(nlh), msg, msg_size);
res = nlmsg_multicast(nl_sk, skb_out, 0, MYGRP, 0);
if (res < 0) {
printk(KERN_INFO "Error while sending bak to user, err id: %d\n", res);
}
}
static int __init
hello_init(void) {
struct netlink_kernel_cfg cfg = {
.groups = MYGRP,
};
printk("Entering: %s\n", __FUNCTION__);
nl_sk = netlink_kernel_create(&init_net, NETLINK_USER, &cfg);
if (!nl_sk) {
printk(KERN_ALERT "Error creating socket.\n");
return -10;
}
send_to_user();
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);
Dado que el módulo del kernel solo enviará el mensaje una vez durante la inicialización, primero ejecuto el programa de escucha y luego inserto el módulo, aunque siempre recibí este error:
Error while sending bak to user, err id: -3
Cuando rastrea el id de error, se refleja en este fragmento de código en netlink/af_netlink.c:
if (info.delivery_failure) {
kfree_skb(info.skb2);
return -ENOBUFS;
}
consume_skb(info.skb2);
if (info.delivered) {
if (info.congested && (allocation & __GFP_WAIT))
yield();
return 0;
}
return -ESRCH;
Supongo que no es delivery_failure pero aún no se entregó por algunas razones.
me referia a esto ejemplo en el que el programa del autor sigue escuchando el cambio de rutas. Aunque me gustaría usar un grupo de multidifusión definido por el usuario.
¿Algunas ideas? ¡Gracias por adelantado!
También tuve un problema similar hace mucho tiempo; puede verificar las siguientes cosas: 1: vea que el valor 31 de NETLINK_USER no está siendo utilizado por otros componentes; si es así, elija algún valor no utilizado. Supongo que 31 está asignado al componente LTT. 2: Al crear un socket en el espacio del usuario, use el mismo valor de NETLINK_USER como se define en el kernel.
int sock = socket(AF_NETLINK,SOCK_RAW, NETLINK_USER);
– Gyan Gupta
28 de marzo de 2014 a las 9:15
¡Gracias por su respuesta! Aunque por lo que entendí, netlink_kernel_create() es el que crea un socket en el espacio del kernel en lugar de socket(), ¿o me estoy perdiendo algo?
– guoger
28 de marzo de 2014 a las 9:49
Quise decir en su programa de espacio de usuario; al abrir el zócalo con
socket(AF_NETLINK,SOCK_RAW,MYPROTO);
hágalo con el mismo valor de NETLINK_USER como lo definió en el kernel. Así que podría ser ` #define NETLINK_USER 30`socket(AF_NETLINK,SOCK_RAW, NETLINK_USER);
– Gyan Gupta
28 de marzo de 2014 a las 10:04
Recuerdo cómo traté de hacer esto. Hay varios ejemplos y tutoriales en Internet y la mayoría de ellos no me funciona. Así que terminé usando la familia netlink genérica y libnl. Puedes ver mi ejemplo de trabajo en github.com/dzeban/keymon
– Alejandro Dzyoba
31 de marzo de 2014 a las 8:50