linuxsocketslinux-kernelnetlink

Unable to Parse generic netlink message from recvfrom buffer


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

Solution

  • 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.