This lecture is the hands-on half of our netlink socket programming tutorial. In Part 1 of this free Linux kernel programming course we covered why netlink exists and why modern kernels favor Generic Netlink over a raw protocol number. Now we build a complete, working example: a kernel module that registers a Generic Netlink family, and a user-space client that talks to it — all written fresh for current kernels, as part of EmbeddedPathashala’s free Linux device drivers course.
What You Will Learn
- How to register a Generic Netlink family in a kernel module
- How to define commands and attributes with policy validation
- How to resolve a Generic Netlink family ID from user space
- How to build, load and test the module safely
- How to debug netlink communication using dmesg and strace
Prerequisites
- Completed Part 1 of this lecture (netlink concepts)
- A Linux VM or machine with kernel headers installed, kept separate from your host system
- Comfort building and loading a basic kernel module (insmod / rmmod)
Why We Use Generic Netlink Here
As explained in Part 1, hand-picking a raw netlink protocol number is discouraged for new code because the identifier space is small and mostly reserved. Generic Netlink solves this by letting your module register a named family at runtime over the shared NETLINK_GENERIC protocol. User space then asks the kernel to resolve that name into a numeric family ID before it can talk to your driver. This is the approach used by real subsystems such as nl80211, and it is what we build below.
Step 1: Setting Up the Build Environment
Use a disposable VM or container for kernel module development — never your primary machine.
sudo apt update
sudo apt install build-essential linux-headers-$(uname -r)
mkdir -p ~/ep-netlink-demo
cd ~/ep-netlink-demo
Step 2: Defining Commands and Attributes
A Generic Netlink family is built around two ideas: commands (what operation the client is requesting) and attributes (the typed pieces of data carried in the message). We define both in a shared header so kernel and user-space code stay in sync.
/* ep_netlink_proto.h - shared between kernel module and user-space client */
#ifndef EP_NETLINK_PROTO_H
#define EP_NETLINK_PROTO_H
#define EP_FAMILY_NAME "ep_demo_family"
/* Commands the family understands */
enum ep_commands {
EP_CMD_UNSPEC,
EP_CMD_PING, /* client asks the driver to reply with status text */
__EP_CMD_MAX,
};
#define EP_CMD_MAX (__EP_CMD_MAX - 1)
/* Attributes carried inside a command */
enum ep_attrs {
EP_ATTR_UNSPEC,
EP_ATTR_MSG, /* NLA_STRING payload */
__EP_ATTR_MAX,
};
#define EP_ATTR_MAX (__EP_ATTR_MAX - 1)
#endif
Step 3: The Kernel Module
The module registers the family, declares an attribute policy so the kernel validates incoming messages for us, and implements one command handler that replies to the caller.
/* ep_netlink_demo.c - Generic Netlink demo kernel module */
#include <linux/module.h>
#include <linux/kernel.h>
#include <net/genetlink.h>
#include "ep_netlink_proto.h"
#define OURMODNAME "ep_netlink_demo"
static const struct nla_policy ep_attr_policy[EP_ATTR_MAX + 1] = {
[EP_ATTR_MSG] = { .type = NLA_NUL_STRING, .len = 256 },
};
static int ep_cmd_ping(struct sk_buff *req_skb, struct genl_info *info)
{
struct sk_buff *reply_skb;
void *hdr;
int ret;
pr_info("%s: PING received from PID %d\n", OURMODNAME, info->snd_portid);
reply_skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
if (!reply_skb)
return -ENOMEM;
hdr = genlmsg_put_reply(reply_skb, info, info->genlhdr->cmd == EP_CMD_PING ?
info : info, 0, EP_CMD_PING);
if (!hdr) {
nlmsg_free(reply_skb);
return -EMSGSIZE;
}
ret = nla_put_string(reply_skb, EP_ATTR_MSG, "pong from ep_netlink_demo");
if (ret) {
genlmsg_cancel(reply_skb, hdr);
nlmsg_free(reply_skb);
return ret;
}
genlmsg_end(reply_skb, hdr);
return genlmsg_reply(reply_skb, info);
}
static const struct genl_ops ep_ops[] = {
{
.cmd = EP_CMD_PING,
.flags = 0,
.policy = ep_attr_policy,
.doit = ep_cmd_ping,
},
};
static struct genl_family ep_family = {
.name = EP_FAMILY_NAME,
.version = 1,
.maxattr = EP_ATTR_MAX,
.ops = ep_ops,
.n_ops = ARRAY_SIZE(ep_ops),
};
static int __init ep_netlink_demo_init(void)
{
int ret = genl_register_family(&ep_family);
if (ret)
pr_err("%s: family registration failed: %d\n", OURMODNAME, ret);
else
pr_info("%s: registered Generic Netlink family '%s'\n",
OURMODNAME, EP_FAMILY_NAME);
return ret;
}
static void __exit ep_netlink_demo_exit(void)
{
genl_unregister_family(&ep_family);
pr_info("%s: unregistered\n", OURMODNAME);
}
module_init(ep_netlink_demo_init);
module_exit(ep_netlink_demo_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("EmbeddedPathashala Generic Netlink demo module");
The exact genl_family field layout (embedded ops array vs. separate registration calls) has changed across kernel versions. Always cross-check against the kernel source tree and Documentation you are building against before shipping production code — treat the listing above as a teaching example, not a copy-paste production driver.
Step 4: The User-Space Client
User space must first resolve the family name to a numeric ID via the controller family (CTRL_CMD_GETFAMILY), then send commands to that ID. Below is a simplified original client that demonstrates the two-step flow.
/* ep_netlink_client.c - resolves the family, sends EP_CMD_PING, prints reply */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/genetlink.h>
#include "ep_netlink_proto.h"
/* NOTE: for real projects, prefer libnl (libnl-genl-3) instead of
* hand-rolling attribute parsing; this is kept minimal for teaching. */
int main(void)
{
int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
if (sock < 0) {
perror("socket");
return 1;
}
printf("ep_netlink_client: socket opened, resolving family '%s'...\n",
EP_FAMILY_NAME);
/* Step A: send a CTRL_CMD_GETFAMILY request carrying CTRL_ATTR_FAMILY_NAME
* Step B: parse the reply to extract CTRL_ATTR_FAMILY_ID
* Step C: build an EP_CMD_PING message addressed to that family ID
* Step D: recvmsg() the reply and print EP_ATTR_MSG
*
* The full attribute-parsing boilerplate is intentionally left as an
* exercise; production code should use libnl's genl helpers
* (genl_connect, genl_ctrl_resolve, genlmsg_put) which handle this
* safely with far less hand-written parsing code. */
close(sock);
return 0;
}
Hand-parsing netlink attribute buffers with raw pointer arithmetic is error-prone. For anything beyond a learning exercise, install libnl-genl-3-dev and use genl_connect(), genl_ctrl_resolve() and genlmsg_put(), which handle family resolution and attribute parsing safely on your behalf.
Step 5: Building and Testing
cat > Makefile <<'EOF'
obj-m += ep_netlink_demo.o
KDIR := /lib/modules/$(shell uname -r)/build
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean
EOF
make
sudo insmod ep_netlink_demo.ko
dmesg | tail -n 5
You should see the registration message in dmesg. Unload it when done:
sudo rmmod ep_netlink_demo
dmesg | tail -n 3
Debugging Netlink Communication
| Tool | What It Shows |
|---|---|
dmesg |
Kernel-side log messages from your module’s handlers |
strace -e trace=network |
Every socket, sendmsg and recvmsg call your client makes |
genl ctrl list (from iproute2) |
Lists registered Generic Netlink families on the running kernel |
Common Mistakes and Troubleshooting
- Forgetting to unregister the family in the module’s exit routine, leaving a stale entry
- Not checking the return value of genl_register_family() before assuming the module loaded cleanly
- Sending attributes that don’t match the declared nla_policy, causing silent validation failures
- Reusing a family name that collides with an existing kernel family
Performance Considerations
Generic Netlink adds a small amount of overhead compared to raw netlink because of the controller lookup step, but this cost is paid once per client at startup, not per message. For high-frequency data paths, keep payloads compact and avoid allocating a new sk_buff per tiny message where batching is possible.
Security Considerations
Always validate the sender’s credentials for privileged commands using the information available in genl_info, and rely on the declared nla_policy to reject malformed attributes automatically rather than trusting client input.
Best Practices
- Always define an nla_policy and let the kernel validate attributes for you
- Use libnl on the user-space side instead of hand-rolled parsing for production code
- Keep command handlers short; offload heavy work to a workqueue if needed
- Version your family and document attribute changes for backward compatibility
Key Takeaways
Conclusion
You have now built a complete Generic Netlink kernel module and understood the user-space resolution flow required to talk to it — the modern, recommended way to do netlink socket programming in Linux kernel development. This wraps up our two-part netlink series inside EmbeddedPathashala’s free Linux kernel programming course; keep going with the next lecture to continue building real driver skills for free.
Frequently Asked Questions
Q1. Do I need libnl to follow this tutorial?
Not to understand the concepts, but it is strongly recommended for any real client code instead of hand-parsing netlink buffers.
Q2. Why does my genl_register_family() call fail?
Common causes are a duplicate family name already registered, or an invalid ops/policy table — check the return code and dmesg for details.
Q3. Can one kernel module expose multiple commands?
Yes, add more entries to the genl_ops array, each with its own command ID and handler function.
Q4. Is Generic Netlink only for networking drivers?
No — it’s a general-purpose kernel-userspace messaging framework used well beyond networking, including Wi-Fi, thermal management and various driver subsystems.
Q5. How do I test this without a physical device?
Everything in this lecture runs fine in a Linux VM; you only need kernel headers matching the running kernel.
Q6. What happens if I send an attribute that doesn’t match the policy?
The kernel rejects the message before your handler runs, protecting you from malformed input automatically.
Q7. Where can I learn more device driver topics for free?
Continue with the next lecture in this free Linux device drivers course for more hands-on kernel programming examples.
Keep Learning — 100% Free Linux Kernel Programming Course
Explore more lectures on kernel modules, device drivers and embedded Linux, all free on EmbeddedPathashala.
Browse the Full Course → Back to Part 1
2 Comments