使用NETLINK_ROUTE通道,无法接收内核模块发送到用户应用程序的定制消息。

18

我正在使用Netlink sockets来发送自定义通知,关于以太网接口状态的内核模块到用户空间应用程序,通过NETLINK_ROUTE通道。我查阅了几篇文章和论文,但它们都演示了一种需要在netlink.h头文件中定义自己的Family(例如NETLINK_TEST),或使用NETLINK_GENERIC的方法。我知道使用NETLINK_ROUTE的套接字已经被内核拥有,因此无法在内核模块中创建它。我无法在用户空间接收消息。非常感谢任何指导。

内核模块:

#include <linux/notifier.h>
#include <asm/kdebug.h> 
#include <linux/netdevice.h>
#include <linux/inetdevice.h>
#include <linux/module.h>
#include <net/sock.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <asm/types.h>
#include <linux/skbuff.h>

MODULE_LICENSE("GPL");

int my_dev_event_handler(struct notifier_block *this, unsigned long event, void *ptr)
{
struct sk_buff *skb = NULL;
struct nlmsghdr *nlh;
int size = 0;
char buf[512];

switch (event) {
    case NETDEV_REGISTER:
            sprintf (buf, "Interface:: %s is Registered with the Notifier...", ((struct net_device *) ptr)->name);
            break;

    case NETDEV_UP:
            sprintf (buf, "Interface:: %s is Up and Running...", ((struct net_device *) ptr)->name);
            break;

    case NETDEV_GOING_DOWN:
            sprintf (buf, "Interface:: %s is going Down...", ((struct net_device *) ptr)->name);
            break;

    case NETDEV_DOWN:
            sprintf (buf, "Interface:: %s is Down...", ((struct net_device *) ptr)->name);
            break;
    }

printk (KERN_INFO "Content of Buf :: %s" , buf);    

size = sizeof(buf);

skb = nlmsg_new(size, GFP_ATOMIC);

if (skb == NULL)
    {
        printk(KERN_ERR "\nError Allocating skb for sending Netlink Message...\n");
        return -1;
    }

nlh = nlmsg_put(skb, 0, 0, NLMSG_DONE, size, 0);
if (nlh == NULL)
    {
        printk(KERN_ERR "\nError putting Netlink Message data into skb...\n");
        goto nlmsg_failure;
        }   

    NETLINK_CB(skb).dst_group = RTNLGRP_LINK;
    strncpy(nlmsg_data(nlh), buf, size);
    nlmsg_end(skb, nlh);

    rtnl_notify(skb, &init_net, 0, RTNLGRP_LINK, nlh, 0);

return 0;

nlmsg_failure:
kfree_skb(skb);
return -EMSGSIZE;

}

static struct notifier_block my_dev_notifier = 
{
.notifier_call = my_dev_event_handler,
};

static int __init my_init (void)
{
printk(KERN_ALERT "***IFM Module Loaded***\n");
register_netdevice_notifier (&my_dev_notifier); 
return 0;
}

static void __exit my_end(void)
{
printk(KERN_ALERT "***IFM Module Unloaded***\n");
unregister_netdevice_notifier (&my_dev_notifier);
}

module_init(my_init);
module_exit(my_end);

用户空间应用程序:

#include <asm/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <net/if.h>
#include <netinet/in.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/types.h>

#define MAX_PAYLOAD 1024

struct sockaddr_nl src_addr, dest_addr;

int read_event (int sockint)
{
int status;
int ret = 0;
char buf[4096];
struct iovec iov = { buf, sizeof(buf) };
struct msghdr msg = { (void *) &dest_addr, sizeof dest_addr, &iov, 1, NULL, 0, 0 };
struct nlmsghdr *h;

h = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));
    memset(h, 0, NLMSG_SPACE(MAX_PAYLOAD));
    h->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
    h->nlmsg_pid = getpid();
    h->nlmsg_flags = 0;

strcpy(NLMSG_DATA(h), "Hello");
printf("Sending message to kernel\n");
    sendmsg(sockint, &msg, 0);

printf("Waiting for message from kernel\n");
memset(h, 0, NLMSG_SPACE(MAX_PAYLOAD));

status = recvmsg (sockint, &msg, 0);

if (status < 0)
    {
            if (errno == EWOULDBLOCK || errno == EAGAIN)
                    return ret;

            printf ("read_netlink: Error recvmsg: %d\n", status);
            perror ("read_netlink: Error: ");
            return status;
    }

    if (status == 0)
    {
            printf ("read_netlink: EOF\n");
    }
printf("\nNo. of Bytes read : %d\n", status);
printf("Received Payload data : %s", NLMSG_DATA (h));
return ret;
}

int main (void)
{
fd_set rfds;
struct timeval tv;
int retval;

int nl_socket = socket (AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);

 if (nl_socket < 0)
 {
 printf ("Socket Open Error!");
 exit (1);
 }

 memset ((void *) &src_addr, 0, sizeof (src_addr));
 src_addr.nl_family = AF_NETLINK;
src_addr.nl_pid = getpid ();
//src_addr.nl_pid = 0;
src_addr.nl_groups = RTMGRP_LINK;
//src_addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR;

if (bind (nl_socket, (struct sockaddr *) &src_addr, sizeof (src_addr)) < 0)
{
    printf ("Socket bind failed!");
    exit (1);
}

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 */

while (1)
{
    FD_ZERO (&rfds);
    //FD_CLR (nl_socket, &rfds);
    FD_SET (nl_socket, &rfds);

    tv.tv_sec = 5;
    tv.tv_usec = 0;

    retval = select (FD_SETSIZE, &rfds, NULL, NULL, &tv);

    if (retval == -1)
        printf ("Error in select() \n");

    else if (retval)
    {
        printf ("Event received >> ");
        read_event (nl_socket);
    }

    else
        printf ("## Select Timed Out ## \n");
}

return 0;
}

1
也许对你来说太晚了,但我还是要说:iov 应该引用 h,而不是 buf。目前你的 h 没有被任何地方使用,所以难怪它不起作用。另外,由于你有两个基本上独立的部分,因此先实现用户空间部分(处理现有消息)是有意义的,这样你就不必同时调试两组代码。 - domen
1个回答

0

我认为你的错误在于应该将buf转换为struct nlmsghdr *,然后填充信息。

char buf[4096];
struct iovec iov = { buf, sizeof(buf) };
struct msghdr msg = { (void *) &dest_addr, sizeof dest_addr, &iov, 1, NULL, 0, 0 };
struct nlmsghdr *h;

memset(buf, 0, sizeof(buf));
h = (struct nlmsghdr *)buf;
h->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
h->nlmsg_pid = getpid();
h->nlmsg_flags = 0;

strcpy(NLMSG_DATA(h), "Hello");
printf("Sending message to kernel\n");
sendmsg(sockint, &msg, 0);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接