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;
}
iov
should referenceh
, and notbuf
. Currently yourh
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