Sample generic Netlink application for virtual network loop back driver

We added the generic netlink functionality to our loopback network driver. The code for the same can be found in this article_link. Now, we develop a sample application and analyze as to what is being performed by the sample application to send a command to the kernel module handling the netlink command.

The sample code for the generic netlink family we developed is provided below:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netlink/netlink.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/ctrl.h>

#define NETLINK_FAMILY_NAME "vnet_nl"
#define NL_CMD_ECHO 1
#define NL_A_MSG    1

static int echo_cb(struct nl_msg *msg, void *arg) {
    struct nlmsghdr *nlh = nlmsg_hdr(msg);
    struct genlmsghdr *ghdr = nlmsg_data(nlh);
    struct nlattr *attrs[2];

    /* Parse attributes */
    genlmsg_parse(nlh, 0, attrs, 1, NULL);
    if (attrs[NL_A_MSG]) {
        printf("Kernel replied: %s\n", nla_get_string(attrs[NL_A_MSG]));
    }
    return NL_OK;
}

int main() {
    struct nl_sock *sock;
    int family_id, ret;

    /* Allocate socket */
    sock = nl_socket_alloc();
    if (!sock) {
        fprintf(stderr, "Failed to allocate netlink socket\n");
        return -1;
    }

    /* Connect to generic netlink */
    if (genl_connect(sock)) {
        fprintf(stderr, "Failed to connect to generic netlink\n");
        nl_socket_free(sock);
        return -1;
    }

    /* Resolve family id */
    family_id = genl_ctrl_resolve(sock, NETLINK_FAMILY_NAME);
    if (family_id < 0) {
        fprintf(stderr, "Family %s not found\n", NETLINK_FAMILY_NAME);
        nl_socket_free(sock);
        return -1;
    }

    /* Build message */
    struct nl_msg *msg = nlmsg_alloc();
    if (!msg) {
        fprintf(stderr, "Failed to allocate message\n");
        nl_socket_free(sock);
        return -1;
    }

    /* Construct generic netlink message */
    void * hdr = genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family_id, 0, 0,
                             NL_CMD_ECHO, 1);
    if (!hdr) {
        fprintf(stderr, "Failed to put genlmsg header\n");
        nlmsg_free(msg);
        nl_socket_free(sock);
        return -1;
    }

    /* Add string attribute */
    nla_put_string(msg, NL_A_MSG, "Hello from user space!");

    /* Set callback for reply */
    nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM, echo_cb, NULL);

    /* Send message */
    ret = nl_send_auto(sock, msg);
    if (ret < 0) {
        fprintf(stderr, "Failed to send message: %d\n", ret);
        nlmsg_free(msg);
        nl_socket_free(sock);
        return -1;
    }

    /* Wait for reply */
    nl_recvmsgs_default(sock);

    nlmsg_free(msg);
    nl_socket_free(sock);
    return 0;
}

The compilation of this sample code requires the libnl and libnl-genl libraries to be installed. The GCC compilation command would require inputs on where the libraries are installed. This is the compilation command that I used in Ubuntu:

As can be seen in the sample code above, the code utilizes a socket to communicate with the kernel unlike the ioctl API used for IOCTLs. We will analyze the code further in the next article.

Analysis of netlink sample application

Leave a Reply

Your email address will not be published. Required fields are marked *