Skip to content

7.2 Examples

1. Read Something in Kernel Space

Following example is a a Loadable Kernel Module (LKM) that creates a file in /proc so you can read something from the kernel space using user-space commands like cat.

LKM That Adds a File to /proc

This module creates a file /proc/helloproc that you can read with cat.

helloproc.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.h> // for copy_to_user()
#define PROC_NAME "helloproc"
#define MESSAGE "Hello from the kernel!\n"
static ssize_t proc_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) {
return simple_read_from_buffer(buf, count, ppos, MESSAGE, strlen(MESSAGE));
}
static struct proc_ops proc_file_ops = {
.proc_read = proc_read,
};
static int __init hello_init(void) {
proc_create(PROC_NAME, 0, NULL, &proc_file_ops);
printk(KERN_INFO "/proc/%s created\n", PROC_NAME);
return 0;
}
static void __exit hello_exit(void) {
remove_proc_entry(PROC_NAME, NULL);
printk(KERN_INFO "/proc/%s removed\n", PROC_NAME);
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("You");
MODULE_DESCRIPTION("Kernel module with /proc file");

πŸ›  Makefile (same as before)

obj-m += helloproc.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

πŸš€ Usage

Terminal window
make
sudo insmod helloproc.ko
cat /proc/helloproc # Output: Hello from the kernel!
sudo rmmod helloproc

You can also run dmesg | tail to see kernel logs.

1.1 Extending the Example

Now your LKM will read and write from a /proc file. You’ll be able to do:

Terminal window
echo "Hi kernel!" > /proc/helloproc
cat /proc/helloproc

and see your message echoed back.

πŸ” Read/Write LKM Using /proc

This module creates /proc/helloproc, and stores a message that you can update with echo.

helloproc_rw.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.h>
#define PROC_NAME "helloproc"
#define MAX_LEN 100
static char message[MAX_LEN] = "Hello from kernel!\n";
static ssize_t message_len = 0;
static ssize_t proc_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) {
return simple_read_from_buffer(buf, count, ppos, message, message_len);
}
static ssize_t proc_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) {
if (count > MAX_LEN) count = MAX_LEN;
if (copy_from_user(message, buf, count)) return -EFAULT;
message_len = count;
return count;
}
static struct proc_ops proc_file_ops = {
.proc_read = proc_read,
.proc_write = proc_write,
};
static int __init hello_init(void) {
proc_create(PROC_NAME, 0666, NULL, &proc_file_ops);
printk(KERN_INFO "/proc/%s created (RW)\n", PROC_NAME);
return 0;
}
static void __exit hello_exit(void) {
remove_proc_entry(PROC_NAME, NULL);
printk(KERN_INFO "/proc/%s removed\n", PROC_NAME);
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("You");
MODULE_DESCRIPTION("Proc RW Kernel Module");

πŸ›  Makefile

obj-m += helloproc_rw.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

πŸ§ͺ Try it out:

Terminal window
make
sudo insmod helloproc_rw.ko
# Read default message
cat /proc/helloproc
# Write a new message
echo "Hi kernel!" | sudo tee /proc/helloproc
# Read the updated message
cat /proc/helloproc
# Clean up
sudo rmmod helloproc_rw

1.2 🧩 Kernel Module: Responds to "uptime"

Now we’ll make a user-space C program that talks to our kernel module via /proc/helloproc. We’ll also change the kernel module a bit to show the current uptime in seconds whenever a user writes "uptime" to it.

helloproc_uptime.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.h>
#include <linux/jiffies.h>
#include <linux/ktime.h>
#define PROC_NAME "helloproc"
#define MAX_LEN 100
static char message[MAX_LEN];
static ssize_t message_len;
static ssize_t proc_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) {
return simple_read_from_buffer(buf, count, ppos, message, message_len);
}
static ssize_t proc_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) {
char input[MAX_LEN];
if (count > MAX_LEN - 1) count = MAX_LEN - 1;
if (copy_from_user(input, buf, count)) return -EFAULT;
input[count] = '\0';
if (strncmp(input, "uptime", 6) == 0) {
unsigned long uptime = jiffies / HZ;
message_len = snprintf(message, MAX_LEN, "Uptime: %lu seconds\n", uptime);
} else {
message_len = snprintf(message, MAX_LEN, "You said: %s", input);
}
return count;
}
static struct proc_ops proc_file_ops = {
.proc_read = proc_read,
.proc_write = proc_write,
};
static int __init hello_init(void) {
proc_create(PROC_NAME, 0666, NULL, &proc_file_ops);
printk(KERN_INFO "/proc/%s created (uptime enabled)\n", PROC_NAME);
return 0;
}
static void __exit hello_exit(void) {
remove_proc_entry(PROC_NAME, NULL);
printk(KERN_INFO "/proc/%s removed\n", PROC_NAME);
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("You");
MODULE_DESCRIPTION("Proc RW Kernel Module with uptime");

πŸ§‘β€πŸ’» User-Space C Program

userprog.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
const char *path = "/proc/helloproc";
FILE *fp;
// Write "uptime" to kernel
fp = fopen(path, "w");
if (!fp) {
perror("fopen write");
return 1;
}
fprintf(fp, "uptime\n");
fclose(fp);
// Read response
fp = fopen(path, "r");
if (!fp) {
perror("fopen read");
return 1;
}
char buffer[128];
while (fgets(buffer, sizeof(buffer), fp)) {
printf("Kernel says: %s", buffer);
}
fclose(fp);
return 0;
}

Compile it:

Terminal window
gcc userprog.c -o userprog

πŸ§ͺ Try It Out

  1. Compile and load your module:

    Terminal window
    make
    sudo insmod helloproc_uptime.ko
  2. Run the user-space program:

    Terminal window
    ./userprog

    βœ… Output:

    Kernel says: Uptime: 12345 seconds
  3. Cleanup:

    Terminal window
    sudo rmmod helloproc_uptime

1.3 Creating a Device File

You’re going full kernel hacker mode now 😎. Let’s level up from /proc to creating a real device file in /dev, using the misc device API, which is much closer to how real drivers work.

πŸ“¦ What’s a misc device?

It’s a simpler way to register a character device. You get:

  • A real file under /dev/mydevice
  • Full control over read, write, ioctl, etc.
  • Clean and minimal boilerplate

πŸ§ͺ Goal

/dev/mydevice that responds to input

Behavior:

  • Write "uptime" β†’ responds with system uptime
  • Write anything else β†’ echoes it back

πŸ”Έ Kernel Module: mydevice.c

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>
#include <linux/jiffies.h>
#define BUFFER_SIZE 256
static char message[BUFFER_SIZE];
static ssize_t message_len;
static ssize_t device_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) {
return simple_read_from_buffer(buf, count, ppos, message, message_len);
}
static ssize_t device_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) {
char input[BUFFER_SIZE];
if (count >= BUFFER_SIZE)
count = BUFFER_SIZE - 1;
if (copy_from_user(input, buf, count))
return -EFAULT;
input[count] = '\0';
if (strncmp(input, "uptime", 6) == 0) {
unsigned long uptime = jiffies / HZ;
message_len = snprintf(message, BUFFER_SIZE, "Uptime: %lu seconds\n", uptime);
} else {
message_len = snprintf(message, BUFFER_SIZE, "You said: %s", input);
}
return count;
}
static struct file_operations fops = {
.owner = THIS_MODULE,
.read = device_read,
.write = device_write,
};
static struct miscdevice my_misc_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = "mydevice",
.fops = &fops,
.mode = 0666
};
static int __init mydevice_init(void) {
int ret = misc_register(&my_misc_device);
if (ret)
printk(KERN_ERR "Failed to register misc device\n");
else
printk(KERN_INFO "/dev/mydevice registered\n");
return ret;
}
static void __exit mydevice_exit(void) {
misc_deregister(&my_misc_device);
printk(KERN_INFO "/dev/mydevice unregistered\n");
}
module_init(mydevice_init);
module_exit(mydevice_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("You");
MODULE_DESCRIPTION("Simple misc device example");

πŸ›  Makefile

obj-m += mydevice.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

πŸ§ͺ Run It!

Terminal window
make
sudo insmod mydevice.ko
# Check that the device was created
ls -l /dev/mydevice
# Interact with it
echo "uptime" | sudo tee /dev/mydevice
cat /dev/mydevice
echo "hello from user" | sudo tee /dev/mydevice
cat /dev/mydevice
sudo rmmod mydevice

πŸ§‘β€πŸ’» Want to write a user-space C client for /dev/mydevice too?

Or maybe want bidirectional communication with a user-space daemon (e.g., using ioctl) or notifications from the kernel (poll, select, or fasync)?

2. Extending netfilter

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
static struct nf_hook_ops netfilter_ops_in;
static struct nf_hook_ops netfilter_ops_out;
unsigned int main_hook(void* priv,
struct sk_buff* skb,
const struct nf_hook_state* state)
{
printk(KERN_INFO "packet received\n");
return NF_ACCEPT; // NF_ACCEPT allows packets, NF_DROP rejects them
}
static int __init hello_init(void)
{
netfilter_ops_in.hook = main_hook; // What function to call?
netfilter_ops_in.pf = PF_INET; // Protocol Family
netfilter_ops_in.hooknum = NF_INET_PRE_ROUTING; // Incoming traffic
netfilter_ops_in.priority = NF_IP_PRI_FIRST; // What priority is this hook?
netfilter_ops_out.hook = main_hook;
netfilter_ops_out.pf = PF_INET;
netfilter_ops_out.hooknum = NF_INET_POST_ROUTING; // Outgoing traffic
netfilter_ops_out.priority = NF_IP_PRI_FIRST;
nf_register_net_hook(&init_net, &netfilter_ops_in); // Register the incoming traffic hook
nf_register_net_hook(&init_net, &netfilter_ops_out); // Register the outgoing traffic hook
return 0;
}
static void __exit hello_exit(void)
{
nf_unregister_net_hook(&init_net, &netfilter_ops_in);
nf_unregister_net_hook(&init_net, &netfilter_ops_out);
}
module_init(hello_init);
module_exit(hello_exit);