Last article Analysis of Linux netfilter hook source code From the perspective of kernel source code, we analyze how hook hooks are executed under the Linux Netfilter framework. This time, we write a simple example code to introduce in detail how to write kernel modules using the Netfilter framework.
In the previous article, we analyzed the data structures used in the Netfilter framework hook from the data structure level. Below, I drew a diagram, starting with the specific examples in the source code:
There is a global variable in the kernel, net_namespace_list linked list. All network namespaces in the system are hung on this linked list. The system default network namespace is init_net, when we add a hook, we hang the new hook on its corresponding linked list.
Let's start writing a simple kernel module based on the netfilter framework.
catalogue
2. First, you need to define an nf_hook_ops
4. Implementation of hook function
1. Declare a hook function
unsigned int packet_filter(unsigned int hooknum, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *));
2. First, you need to define an nf_hook_ops
① We process network packets at the ip layer (network layer), where. pf = NFPROTO_INET;
② The hook point is on the preouting chain, here hooknum = NF_INET_PRE_ROUTING;
③ hook functions are executed on this chain with the highest priority, i.e priority = NF_IP_PRI_FIRST;
④ Set the hook function to the function we defined earlier, that is hook = (nf_hookfn *)packet_filter.
static struct nf_hook_ops packet_simple_nf_opt = { .hook = (nf_hookfn *)packet_filter, .pf = NFPROTO_INET, .hooknum = NF_INET_PRE_ROUTING, .priority = NF_IP_PRI_FIRST, };
3. Register this nf_hook_ops
Register the NF in the init function of the kernel module_ hook_ ops
static int simple_nf_init(void) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,3,0) nf_register_net_hook(&init_net, &packet_simple_nf_opt); #else nf_register_hook(&packet_simple_nf_opt); #endif printk("[simple_nf_test] network hooks success.\n"); return 0; }
4. Implementation of hook function
unsigned int packet_filter(unsigned int hooknum, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { int ret = NF_DROP; struct iphdr *iph; struct tcphdr *tcph; struct udphdr *udph; printk("[simple_nf_test] %s. start.....\n", __func__); if(skb == NULL) return NF_ACCEPT; iph = ip_hdr(skb); if(iph == NULL) return NF_ACCEPT; printk("[simple_nf_test] %s. protocol is [%d].\n", __func__, iph->protocol); printk("[simple_nf_test] %s. source addr is [%pI4].\n", __func__, &iph->saddr); printk("[simple_nf_test] %s. dest addr is [%pI4].\n", __func__, &iph->daddr); switch(iph->protocol) { case IPPROTO_TCP: tcph = (struct tcphdr *)(skb->data + (iph->ihl * 4)); printk("[simple_nf_test] %s. tcp source port is [%d].\n", __func__, ntohs(tcph->source)); printk("[simple_nf_test] %s. tcp dest port is [%d].\n", __func__, ntohs(tcph->dest)); break; case IPPROTO_UDP: udph = (struct udphdr *)(skb->data + (iph->ihl * 4)); printk("[simple_nf_test] %s. udp source port is [%d].\n", __func__, ntohs(udph->source)); printk("[simple_nf_test] %s. udp dest port is [%d].\n", __func__, ntohs(udph->source)); break; default : return NF_ACCEPT; } printk("[simple_nf_test] %s. end.\n\n\n", __func__); return NF_ACCEPT; }
In the hook function, I only print the source, destination ip and port information.
5. Sign out of custom hook
To log off the previously customized hook when the kernel module exits:
static void simple_nf_exit(void) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,3,0) nf_unregister_net_hook(&init_net, &packet_simple_nf_opt); #else nf_unregister_hook(&packet_simple_nf_opt); #endif printk("[simple_nf_test] remove hook lkm success!\n"); }
6. Write Makefile
obj-m += my_nf_lkm.o my_nf_lkm-objs := simple_nf_test.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
7. Required header file
#include <linux/module.h> #include <linux/kernel.h> #include <linux/version.h> #include <linux/ip.h> #include <linux/tcp.h> #include <linux/udp.h> #include <linux/netfilter.h> #include <linux/netfilter_ipv4.h>
8. Compile and insert modules
[root@yg-test-centos8 simple_test]# ls Makefile simple_nf_test.c [root@yg-test-centos8 simple_test]# make make -C /lib/modules/4.18.0-80.el8.x86_64/build M=/home/nf_test/simple_test modules make[1]: Entering directory '/usr/src/kernels/4.18.0-80.el8.x86_64' CC [M] /home/nf_test/simple_test/simple_nf_test.o LD [M] /home/nf_test/simple_test/my_nf_lkm.o Building modules, stage 2. MODPOST 1 modules CC /home/nf_test/simple_test/my_nf_lkm.mod.o LD [M] /home/nf_test/simple_test/my_nf_lkm.ko make[1]: Leaving directory '/usr/src/kernels/4.18.0-80.el8.x86_64' [root@yg-test-centos8 simple_test]# [root@yg-test-centos8 simple_test]# [root@yg-test-centos8 simple_test]# [root@yg-test-centos8 simple_test]# insmod my_nf_lkm.ko [root@yg-test-centos8 simple_test]#
The system log is shown in the figure below:
9. Unloading module
After the final test, don't forget to uninstall the kernel module:
[root@yg-test-centos8 simple_test]# rmmod my_nf_lkm [root@yg-test-centos8 simple_test]#
That's it.