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.