I am trying to get some events from kernel to User space via generic netlink family sockets.
I have implemented the communication following this link and my user space callback is getting called. However I am getting segmentation fault when parsing the message. Instead of using nl_socket_modify_cb
to setup the callback handler, I have used libev ev_io_init
to setup a handler whenever it becomes ready to READ
. In the sample code below I have omitted the error handling since, my callback function is getting called when kernel sends a multicast message. So everything seems to work till that point.
Sample code:
ev_io *ei;
struct nl_sock *mcsk;
/* Allocate netlink socket and connect to generic netlink */
int conn()
{
if (!mcsk) {
mcsk = nl_socket_alloc();
if (!mcsk) {
return -ENOMEM;
}
return genl_connect(mcsk);
}
return 0;
}
/* Disconnect and release socket */
void disconn()
{
if (mcsk) {
nl_close(mcsk);
nl_socket_free(mcsk);
}
}
ret = conn();
nl_socket_disable_seq_check(mcsk);
nl_socket_disable_auto_ack(mcsk);
int fam = genl_ctrl_resolve(mcsk, GENLTEST_GENL_NAME);
int mcgrp = genl_ctrl_resolve_grp(mcsk, GENLTEST_GENL_NAME, GENLTEST_MC_GRP_NAME);
ret = nl_socket_add_membership(mcsk, mcgrp)
ev_io_init(ei, nl_arp_event_cb, nl_socket_get_fd(mcsk), EV_READ);
ev_io_start(gloop, ei);
void nl_arp_event_cb(EV_P_ ev_io *w, int revents) {
char buf[256];
int left;
struct sockaddr_nl from;
socklen_t fromlen;
fromlen = sizeof(from);
left = recvfrom(w->fd, buf, sizeof(buf), MSG_DONTWAIT,
(struct sockaddr *) &from, &fromlen);
if (left < 0) {
if (errno != EINTR && errno != EAGAIN)
PR_ERR( "netlink: recvfrom failed: %s", strerror(errno));
return;
}
struct nl_msg *msg = (struct nl_msg*)buf;
struct nlattr *tb[GENLTEST_A_MAX + 1];
//struct genlmsghdr *genlhdr = nlmsg_data(nlmsg_hdr(msg));
genlmsg_parse(nlmsg_hdr(msg), 0, tb, GENLTEST_A_MAX, NULL);
if (!tb[GENLTEST_A_MSG]) {
PR_ERR("GNA: msg attribute missing from message");
return;
}
PR_INFO("GNA: msg rcvd: %s", nla_get_string(tb[GENLTEST_A_MSG]));
return;
}
The only difference between the example netlink code in the link and mine is that there callback is getting the message as struct nl_msg *msg,
while I am calling recvfrom and putting the message in the buffer. Then trying to parse it.
Any example how to parse the message obtained from recvfrom ?
My gdb backtrace shows:
(gdb) bt
#0 0x76c7e564 in nlmsg_valid_hdr () from target/usr/lib/libnl-3.so.200
#1 0x76c625f0 in genlmsg_valid_hdr () from target/usr/lib/libnl-genl-3.so.200
#2 0x76c62734 in genlmsg_parse () from target/usr/lib/libnl-genl-3.so.200
#3 0x00013d12 in nl_arp_event_cb (loop=<optimized out>, w=0x8fe738, revents=<optimized out>) at arp_inspection.c:48
#4 0x76dd1364 in ev_invoke_pending () from target/usr/lib/libev.so.4
#5 0x76dd33bc in ev_run () from target/usr/lib/libev.so.4
#6 0x00000006 in ?? ()
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
(gdb) f 3
#3 0x00013d12 in nl_arp_event_cb (loop=<optimized out>, w=0x8fe738, revents=<optimized out>) at arp_inspection.c:48
48 genlmsg_parse(nlmsg_hdr(msg), 0, tb, GENLTEST_A_MAX, NULL);
I followed the below link and able to parse the message although that example is not complete in terms of parsing multiple attributes but still some starting point.
http://www.electronicsfaq.com/2014/02/generic-netlink-sockets-example-code.html
Edit: Actually, we just need the pointer to struct genlmsghdr into the buf obtained from recv, once that is done, you can follow this link and use nla_parse to obtain all attributes in a single call.