- Firewall loadable kernel module (LKM)
I write program with C for LKM to filtering input packet.
I use netfilter to check packet ip and port,to check what to do with this packet by Rules set.
I write character driver too.for Receiving congif for filtering policy from user space.
at first by default our program policy is blacklist with 0 ip:port to blocking. so at first all packet Accepted !!
after running test.c, it sent text in config.txt.
our firewall check what text receiving.if its equal to "blacklist" , our policy changed to black list ,
if text contain "whitelist" , our policy changed to white list ,
and if is contain "ip:port" , its add to list of our last policy.
NOTE : if policy chaned , list will be empty!!!
A loadable kernel module (LKM) is a mechanism for adding code to, or removing code from, the Linux kernel at run time. They are ideal for device drivers, enabling the kernel to communicate with the hardware without it having to know how the hardware works. The alternative to LKMs would be to build the code for each and every driver into the Linux kernel.
$ sudo apt-get install git
$ git clone https://github.com/mahdi2019/firewall.git
The system must be prepared to build kernel code, and to do this you must have the Linux headers installed on your device. On a typical Linux desktop machine you can use your package manager to locate the correct package to install. For example, under 64-bit Debian you can use :
first know your current kernel version :
$ uname -r
then check linux-header installed or not :
$ ls -l /usr/src/linux-headers-$(uname -r)
if not installed , so should installed :
$ sudo apt update
$ apt search linux-headers-$(uname -r)
$ sudo apt install linux-headers-$(uname -r)
It is very easy to crash the system when you are writing and testing LKMs. It is always possible that such a system crash could corrupt your file system — it is unlikely, but it is possible. Please back up your data and/or use an Virtual Machine,
Some of the key differences are that kernel modules:
- for a start there is no main() function!
- do not execute sequentially — a kernel module registers itself to handle requests using its initialization function, which runs and then terminates. The type of requests that it can handle are defined within the module code. This is quite similar to the event-driven programming model that is commonly utilized in graphical-user interface (GUI) applications.
- do not have automatic cleanup — any resources that are allocated to the module must be manually released when the module is unloaded, or they may be unavailable until a system reboots.
- do not have printf() functions — kernel code cannot access libraries of code that is written for the Linux user space. The kernel module lives and runs in kernel space, which has its own memory address space. The interface between kernel space and user space is clearly defined and controlled. We do however have a printk() function that can output information, which can be viewed from within user space.
- can be interrupted — one conceptually difficult aspect of kernel modules is that they can be used by several different programs/processes at the same time. We have to carefully construct our modules so that they have a consistent and valid behavior when they are interrupted.
- have a higher level of execution privilege — typically, more CPU cycles are allocated to kernel modules than to user-space programs. This sounds like an advantage, however, you have to be very careful that your module does not adversely affect the overall performance of your system.
- do not have floating-point support — it is kernel code that uses traps to transition from integer to floating-point mode for your user space applications. However, it is very difficult to perform these traps in kernel space. The alternative is to manually save and restore floating point operations — a task that is best avoided and left to your user-space code.
my solution isn't the best way , but it's work :D
Netfilter is a packet filtering subsystem in the Linux kernel stack and has been there since kernel 2.4.x. Netfilter's core consists of five hook functions declared in linux / netfilter_ipv4.h. Although these functions are for IPv4, they are not much different from those used in the IPv6 counterpart. The hooks are used to analyze packets in various locations on the network stack.
that can declaration hook in path of network packets.
With the help of this hooks , at different points of the packet path in the Linux kernel , can get them and check or modify them as needed, then return them or delete them from the continue.
This hooks only can use in kernel space and can not use in user space.
[INPUT]--->[1]--->[ROUTE]--->[3]--->[4]--->[OUTPUT]
| ^
| |
| [ROUTE]
v |
[2] [5]
| ^
| |
v |
[INPUT*] [OUTPUT*]
[1] NF_IP_PRE_ROUTING (Right after the packets have been received. )
[2] NF_IP_LOCAL_IN (Packets addressed to the network stack. )
[3] NF_IP_FORWARD (Packets that should be forwarded. )
[4] NF_IP_POST_ROUTING (Packets that have been routed and are ready to leave)
[5] NF_IP_LOCAL_OUT (Packets from our own network stack)
[*] Network Stack
- NF_DROP : drop the packet (don't continue trip)
- NF_ACCEPT : accept the packet (continue network stack trip)
- NF_STOLEN : hook steals the packet (don't continue trip)
- NF_QUEUE : queue the packet to userspace
- NF_REPEAT : repeat the hook function
declarated in linux/netfilter_ipv4.h(Link)
enum nf_ip_hook_priorities {
NF_IP_PRI_FIRST = INT_MIN,
NF_IP_PRI_CONNTRACK_DEFRAG = -400,
NF_IP_PRI_RAW = -300,
NF_IP_PRI_SELINUX_FIRST = -225,
NF_IP_PRI_CONNTRACK = -200,
NF_IP_PRI_MANGLE = -150,
NF_IP_PRI_NAT_DST = -100,
NF_IP_PRI_FILTER = 0,
NF_IP_PRI_SECURITY = 50,
NF_IP_PRI_NAT_SRC = 100,
NF_IP_PRI_SELINUX_LAST = 225,
NF_IP_PRI_CONNTRACK_CONFIRM = INT_MAX,
NF_IP_PRI_LAST = INT_MAX,
};
typedef unsigned int nf_hookfn(unsigned int hooknum,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *));
- hooknum : Indicates the point at which the hook is recorded
- skb : A pointer to a structure that contains packet information
- in : Indicates the input network interface
- out : Indicates the output network interface
- The last parameter : The pointer to a function called by the netfilter itself after all the hooks are done and usually the hook functions are not called it because it causes the other hooks can't do their job.
We do this using the nf_register_hook
function. The input parameter of this function is a variable of type nf_hook_ops
. All the hook information is set to this variable and then the function is recorded in the netfilter subsystem.
struct nf_hook_ops {
struct list_head list;
/* User fills in from here down. */
nf_hookfn *hook;
struct module *owner;
u_int8_t pf;
unsigned int hooknum;
/* Hooks are ordered in ascending priority. */
int priority;
};
- hook : pointer to hook finction
- owner : Indicates a module in which this function is defined and registered. The THIS_MODULE macro is usually used for this area.
- pd : The protocol specifies the packets the function wants to receive, and here we use NFPROTO_IPV4 to receive IPv4 packets.(The rest of the protocols can be found in the linux/socket.h file.
- hooknum : Indicates the point where you want to record the hook. Must be one of the values mentioned above. Here we use
NF_INET_PRE_ROUTING
to get all incoming packets. - priority : To specify the priority of this hook, we use the
NF_IP_PRI_FIRST
macro value here.
Linux/netfilter.h(Link)
A Makefile is required to build the kernel module — in fact, it is a special kbuild Makefile. The kbuild Makefile required to build the kernel module. Makefile Required to Build the LKM
obj-m+=firewall.o
all:
make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) modules
$(CC) set_config.c -o test
clean:
make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) clean
rm test
The first line of this Makefile is called a goal definition and it defines the module to be built (firewall.o).
The syntax is surprisingly intricate, for example obj-m
defines a loadable module
goal, whereas obj-y
indicates a built-in object
goal. The syntax becomes more complex when a module is to be built from multiple objects, but this is sufficient to build this example LKM.
The reminder of the Makefile is similar to a regular Makefile.
The $(shell uname -r)
is a useful call to return the current kernel build version — this ensures a degree of portability for the Makefile.
The -C
option switches the directory to the kernel directory before performing any make tasks.
The M=$(PWD)
variable assignment tells the make command where the actual project files exist.
The modules
target is the default target for external kernel modules.
An alternative target is modules_install
which would install the module (the make command would have to be executed with superuser permissions and the module installation path is required).
This module can now be loaded using the kernel module tools as follows:
$ make
$ sudo insmod firewall.ko
To see list of all module :
$ lsmod
You can get information about the module using the modinfo command, which will identify the description, author and any module parameters that are defined:
$ modinfo firewall.ko
$ sudo rmmod firewall.ko
You can repeat these steps and view the output in the kernel log that results from the use of the printk()
function. I recommend that you use a second terminal window and view the output as your LKM is loaded and unloaded, as follows:
$ journalctl -f
$ make clean
mahdi heidari - mahdi2019
This project is licensed under the MIT License.
- complete code
- complete Document