Free Linux Kernel Programming
Intermediate
6.x (Modern)
3 of 3
← Previous Lecture | Next Lecture →
We now complete the picture from this free Linux kernel programming course: the kernel module that receives the message our user space client sends, logs it, and sends a reply. This lecture uses the modern netlink_kernel_create() API as it exists on current 6.x kernels, along with the associated cleanup you need for a leak-free module.
What You Will Learn
Prerequisites
This lecture assumes you’ve completed the previous two lectures on netlink concepts and user space programming, and that you’re already comfortable writing a basic loadable kernel module with module_init()/module_exit().
Step 1: Register the Netlink Kernel Socket
On the kernel side, you don’t call socket() and bind() the way user space does. Instead, you call netlink_kernel_create() once, typically from your module’s init function, passing a struct netlink_kernel_cfg that describes your callback function. This registers your module as the kernel-side endpoint for the chosen protocol number.
#include <linux/module.h>
#include <linux/netlink.h>
#include <net/sock.h>
#define EP_NETLINK_PROTO 25
static struct sock *ep_nl_sock;
static void ep_nl_recv_msg(struct sk_buff *skb);
static struct netlink_kernel_cfg ep_nl_cfg = {
.input = ep_nl_recv_msg,
};
static int __init ep_netlink_init(void)
{
ep_nl_sock = netlink_kernel_create(&init_net, EP_NETLINK_PROTO, &ep_nl_cfg);
if (!ep_nl_sock) {
pr_err("ep_netlink: failed to create netlink socket\n");
return -ENOMEM;
}
pr_info("ep_netlink: kernel netlink socket ready\n");
return 0;
}
Step 2: Write the Receive Callback
Every time a user space process sends a message on this protocol, the kernel invokes your input callback with the raw struct sk_buff. You extract the nlmsghdr from it, read the sender’s port ID (so you know who to reply to), and pull out the payload.
static void ep_nl_recv_msg(struct sk_buff *skb)
{
struct nlmsghdr *nlh;
int sender_pid;
struct sk_buff *reply_skb;
const char *reply_text = "message received by kernel";
int msg_size = strlen(reply_text) + 1;
nlh = (struct nlmsghdr *)skb->data;
pr_info("ep_netlink: received: %s\n", (char *)nlmsg_data(nlh));
sender_pid = nlh->nlmsg_pid;
reply_skb = nlmsg_new(msg_size, GFP_KERNEL);
if (!reply_skb) {
pr_err("ep_netlink: failed to allocate reply skb\n");
return;
}
nlh = nlmsg_put(reply_skb, 0, 0, NLMSG_DONE, msg_size, 0);
strcpy(nlmsg_data(nlh), reply_text);
nlmsg_unicast(ep_nl_sock, reply_skb, sender_pid);
}
Step 3: Clean Up on Module Exit
Forgetting to release the netlink kernel socket is one of the most common causes of a kernel module that can’t be safely unloaded and reloaded. Always call netlink_kernel_release() in your exit function.
static void __exit ep_netlink_exit(void)
{
netlink_kernel_release(ep_nl_sock);
pr_info("ep_netlink: kernel netlink socket released\n");
}
module_init(ep_netlink_init);
module_exit(ep_netlink_exit);
MODULE_LICENSE("GPL");
Security Considerations
- Never trust the payload length blindly — validate
nlh->nlmsg_lenbefore reading past it to avoid out-of-bounds access. - Consider checking the sender’s credentials (available via
NETLINK_CB(skb)) if your protocol should be restricted to privileged callers. - Avoid echoing untrusted input directly into
printk/pr_infoformat strings — always use a format specifier like%s.
Performance Considerations
Netlink messages are queued, and under sustained high-frequency traffic the receive queue can fill up, causing messages to be dropped rather than blocking the sender. If your driver needs guaranteed delivery of every event, design an application-level acknowledgment scheme on top of netlink rather than assuming delivery.
Common Mistakes and Troubleshooting
| Symptom | Likely Cause |
|---|---|
| Module fails to load, insmod errors | Protocol number already registered by another module |
| User space never gets a reply | nlmsg_unicast() called with the wrong sender_pid |
| Kernel warning on module unload | Missing netlink_kernel_release() call |
Key Takeaways
A kernel-side netlink handler is registered once via netlink_kernel_create() with a callback that fires on every incoming message. Inside that callback you read the sender’s port ID from the message header and reply with nlmsg_unicast(). Always release the socket in your exit function to keep the module safely reloadable.
Frequently Asked Questions
Q1. Does netlink_kernel_create() block waiting for messages?
No, it just registers the callback; message delivery happens asynchronously whenever user space sends data.
Q2. Can multiple user space processes talk to the same kernel netlink family?
Yes, each with its own unique port ID, and the kernel can reply to each individually using nlmsg_unicast().
Q3. What’s the difference between nlmsg_unicast() and broadcasting?nlmsg_unicast() replies to a single specific port ID, while broadcast delivery sends to every process subscribed to a multicast group.
Q4. Do I need generic netlink instead of this raw approach for a real driver?
For new, general-purpose drivers, generic netlink is usually recommended, but understanding the raw approach first makes the generic netlink API far easier to follow.
Q5. Why does my callback run in a strange context?
Netlink receive callbacks can run in a context where sleeping isn’t always safe depending on configuration — avoid blocking calls inside the callback unless you’ve confirmed it’s safe for your setup.
Continue exploring the free Linux kernel programming course and free Linux device drivers course for more hands-on kernel module tutorials.
Next Lecture → Back to Course Index
2 Comments