Creating a Netfilter kernel module which filters UDP packets
by Paul Kiddie • November 2, 2009 • kernel module, linux, netfilter, networking • 18 Comments
Last time we created a Netfilter kernel module which simply dropped all packets which gave the structure and functions that need to be implemented for a Netfilter module to work.
This time, we’ll extend the functionality to poke into the IP header, specifically the protocol field, in order to perform functionality specific to a packet type. I used an old article as the basis of this which can be found at http://www.linuxjournal.com/article/7184, but as you’ll see in this article, access to packet headers has changed significantly between Linux kernel 2.4 and 2.6. This tutorial is written for Ubuntu 8.04, with the development environment set up as specified in part 1, so for those of you with other distributions, YMMV.
Specifically, in kernel 2.6, access to network, transport and mac headers are now not through the fields nh
, h
and mac
on the skbuff
structure any more, but through new accessors: skb_network_header
, skb_transport_header
and skb_mac_header
. To determine this, I had to look at the header files for the given data structure, in this case, skbuff
. Having installed the linux-headers package earlier, we simply navigate to the installation folder, /lib/modules/2.6.24-23-generic/build/include/linux
, and checking out skbuff.h
we see that in the definition of the sk_buff
data structure that nh
, h
and mac
fields no longer exist. What we do see though are several accessor methods:
static inline unsigned char *skb_transport_header(const struct sk_buff *skb)
{ ... }
static inline unsigned char *skb_network_header(const struct sk_buff *skb)
{ ... }
static inline unsigned char *skb_mac_header(const struct sk_buff *skb)
.
{ ... }
In this example, we will use the skb_network_header
accessor method to access the ip packet’s protocol number. If the packet’s protocol number corresponds to UDP (17, from http://www.iana.org/assignments/protocol-numbers/), I log this fact in the /var/log/messages. Additional header files are included to cast the return values from skb_network_header
and skb_transport_header
to the appropriate iphdr
and udphdr
structures. Note in this example no operations are performe udp packet header we cast, but you can access any of the fields available to you — check out udp.h
and ip.h
for the available fields.
So, without further ado, the complete code:
- //’Hello World’ v2 netfilter hooks example
- //For any packet, get the ip header and check the protocol field
- //if the protocol number equal to UDP (17), log in var/log/messages
- //default action of module to let all packets through
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/netfilter.h>
- #include <linux/netfilter_ipv4.h>
- #include <linux/skbuff.h>
- #include <linux/udp.h>
- #include <linux/ip.h>
- static struct nf_hook_ops nfho; //net filter hook option struct
- struct sk_buff *sock_buff;
- struct udphdr *udp_header; //udp header struct (not used)
- struct iphdr *ip_header; //ip header struct
- unsigned int hook_func(unsigned int hooknum, struct sk_buff **skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
- {
- sock_buff = *skb;
- ip_header = (struct iphdr *)skb_network_header(sock_buff); //grab network header using accessor
- if(!sock_buff) { return NF_ACCEPT;}
- if (ip_header->protocol==17) {
- udp_header = (struct udphdr *)skb_transport_header(sock_buff); //grab transport header
- printk(KERN_INFO "got udp packet \n"); //log we’ve got udp packet to /var/log/messages
- return NF_DROP;
- }
- return NF_ACCEPT;
- }
- int init_module()
- {
- nfho.hook = hook_func;
- nfho.hooknum = NF_IP_PRE_ROUTING;
- nfho.pf = PF_INET;
- nfho.priority = NF_IP_PRI_FIRST;
- nf_register_hook(&nfho);
- return 0;
- }
- void cleanup_module()
- {
- nf_unregister_hook(&nfho);
- }
So whats changed from the example in part 2?
- now using header files,
skbuff.h
,ip.h
andudp.h
, which allows us to cast the relevant data structures we need to poke around - new logic in
hook_func
to get the protocol number, and if this is equal to 17, to log that we have received a UDP packet to /var/log/messages
In the final part, we will seperate the kernel module into a user space daemon and a smaller kernel module. This brings many advantages, such as easier debugging and less likelihood of causing a kernel panic from the seperation of logic (such as from common programming mistakes leading to seg faults). To acheive this communication between the user space and kernel module, we will use the standard mechanism: netlink.
Pingback: Paul Kiddie’s Blog : Creating a “hello world” kernel module in linux
Hi Paul,
Thank you very much for the kind reply. Your second article helped me a lot. I have small doubt, if I want to acess the source and destination address from the ip header of the packet, is there any way to do it? As we are looking for INET protocol family (pf = PF_INET) we should capture all ip packets and all packets should contain source address. With this back ground I have tried using the saddr variable which is in iphdr structure (iphdr->saddr) but it gave me segmentation fault. I am not able to debug this fault, my system just hangs.
Is there any other way to do it? Is acess to the saddr field of iphdr structure is legal? or is there any other method to access it?
Hi Paul,
Thanks for this series of articles – they’ve been very useful so far.
One thing, is that neither this one nor the previous seem to work on Ubuntu 9 (2.6.28-16 kernel). The errors look like this:
[email protected]:~/projects/netfilter-test$ make
make -C /lib/modules/2.6.28-16-generic/build SUBDIRS=/home/bje/projects/netfilter-test modules
make[1]: Entering directory `/usr/src/linux-headers-2.6.28-16-generic’
CC [M] /home/bje/projects/netfilter-test/nfudp.o
/home/bje/projects/netfilter-test/nfudp.c: In function ‘init_module’:
/home/bje/projects/netfilter-test/nfudp.c:42: warning: assignment from incompatible pointer type
/home/bje/projects/netfilter-test/nfudp.c:43: error: ‘NF_IP_PRE_ROUTING’ undeclared (first use in this function)
/home/bje/projects/netfilter-test/nfudp.c:43: error: (Each undeclared identifier is reported only once
/home/bje/projects/netfilter-test/nfudp.c:43: error: for each function it appears in.)
make[2]: *** [/home/bje/projects/netfilter-test/nfudp.o] Error 1
make[1]: *** [_module_/home/bje/projects/netfilter-test] Error 2
make[1]: Leaving directory `/usr/src/linux-headers-2.6.28-16-generic’
make: *** [default] Error 2
Examining linux/netfilter_ipv4.h seems to indicate that an #ifndef __KERNEL__ now extends further than it used to and is preventing NF_IP_PRE_ROUTING from being visible to the module. Something similar is suggested by:
http://markmail.org/message/kvzvzc32v3htduin#query:netfilter_ipv4.h%20changes%20kernel+page:1+mid:chddq75k2cxjzuqr+state:results
Any thoughts as to what needs to be done to change these examples to work with a 2.6.28-16 or thereabouts kernel?
Hi paul,
I am a beginer .I tried inserting this module and module insertion was successfull.But wen i sent traffic , system hangs.Any suggestion on what can be done for this problem.I changed NF_IP_PRE_ROUTING as NF_INET_PRE_ROUTING.
Hi Paul
Congrats , I think you have the best tutorial explaining netfilter kernel module.
If we make drastic changes to the packet that the packet is not at all recognizable and we want to send the packets back to the pre-routing, do we have to do something special?
Hi I suppose my reply is very late but if anyone is still having problem: ‘NF_IP_PRE_ROUTING’ undeclared (first use in this function)
try using 0 instead of ‘NF_IP_PRE_ROUTING’ because NF_IP_PRE_ROUTING=0;
Also when i run you UDP capture example the system freezes any suggestion… i m using ubuntu 8.04
thanks
Similar freeze for me on ubuntu 10.04. I am using a similar code. To me it looks like whenever I try to read “ip_header” extracted from sk_buff, i get the freeze.
to get rid of freezing problem change
sk_buff **skb
to
sk_buff *skb
and
sock_buff = * skb to
sock_buff = skb
and for nfho.hooknum = NF_IP_PRE_ROUTING error
either set it to 0
or change it to
nfho.hooknum = NF_INET_PRE_ROUTING; ( not sure)
Hey Folks,
Had the same problem so, do the trick;)
#undef __KERNEL__
#include /* for constants like NF_IP_PRI_FIRST */
#define __KERNEL__
Regards,
Karol
I try to do something more : modify tcp header and then calculate tcp checksum again by using Netfilter. Can you help me to do with this.
HI Paul,
Thank You Very Much for the Information. It helped me a lot. I am trying to add an Extra IP header over the Received Ping Packet. I have increased the size of skb by 20 bytes and filled the IP Header Fields accordingly.
After adding the IP header ; I have return NF_STOLEN. Please let me know ; What is the Function that I should call to forward the STOLEN Packet?? OR Please Point me to some Documentation from where I can get the necessary information ???
-Thanks in Advance,
VKS
Paul, thanks for the simple yet detailed article! It helped a lot.
And thanks Sunil Shah for his tip that helped me get over the freeze issue (2.6 kernal).
paul i have modified ur code to print source and destination address of the packet….now i’m trying to get read the data from the packet but havent got any way i tried to print the data using sk_buff->data but even if i put it in loop skb_underpannic is called…. any suggestions how to access the data?
Hi,Paul. Thanks for the great article. Where could somebody find the final part(as was mentioned above),using netlink?
Hi Paul,
Your post is so worthy, and easy to understand; thanks!
I wonder if you wrote thread for the final part(user-space and kernel space based Linux Firewall).
For those who are searching for such topic, thanks to Liu Feipeng, in the following link, he has written a thorough thread about Linux firewall,
http://www.roman10.net/how-to-write-a-linux-firewall-in-less-than-1000-lines-of-codeput-everything-together/
Happy hacking!
Pingback: Linux kernel programming(III) | BaremaNet
Hi Paul, you wrote very helpful series of article on Netfilter, thanks!