Creating a Netfilter kernel module which filters UDP packets
November 02, 2009
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 #include #include #include #include #include #include static struct nf\_hook\_ops nfho; //net filter hook option structstruct sk\_buff \*sock\_buff;struct udphdr \*udp\_header; //udp header struct (not used)struct iphdr \*ip\_header; //ip header structunsigned 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 accessorif(!sock\_buff) { return NF\_ACCEPT;}if (ip\_header->protocol==17) {udp\_header = (struct udphdr \*)skb\_transport\_header(sock\_buff); //grab transport headerprintk(KERN\_INFO "got udp packet \\n"); //log we've got udp packet to /var/log/messagesreturn 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.