Unable to receive customized message sent from kernel module to user application using NETLINK_ROUTE channel
Asked Answered
C

1

18

I am working with Netlink sockets to send customized notifications regarding the state of an ethernet interface from a kernel module to a user space application over the NETLINK_ROUTE channel. I have gone through several articles and papers but all of them demonstrate an approach where you need to define your own family e.g. NETLINK_TEST in the netlink.h header or by using NETLINK_GENERIC. I am aware that the socket using NETLINK_ROUTE is already owned by the kernel so one cannot create it in the kernel module. I am unable to receive the message in the user space. Any guidance will be highly appreciated. So here are the two codes:

Kernel Module:

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

User space application:

#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;
}
Caught answered 19/8, 2014 at 7:50 Comment(1)
Probably too late for you, but here it goes: iov should reference h, and not buf. Currently your h isn't used anywhere, so no wonder it doesn't work. Btw. since you have 2 mostly separate parts, it makes sense to first implement userspace part (that deals with existing messages), so you don't have to debug two sets of code at once.Yazbak
I
0

I think your mistake you should cast buf to struct nlmsghdr * and then fill the info.

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);
Insistency answered 21/6, 2015 at 14:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.