Netfilter-Like Kernel Module to Get Source and Destination Address

netfilter-like kernel module to get source and destination address

This article is a little outdated now. Text that you don't understand is only applicable to kernel versions below 3.11.

For new kernels (>= 3.11)

If you are sure that your code will only be used with kernels >= 3.11, you can use next code for both input and output packets:

udp_header = (struct udphdr *)skb_transport_header(skb);  

Or more elegant:

udp_header = udp_hdr(skb);

It's because transport header is already set up for you in ip_rcv():

skb->transport_header = skb->network_header + iph->ihl*4;

This change was brought by this commit.

For old kernels (< 3.11)

Outgoing packets (NF_INET_POST_ROUTING)

In this case .transport_header field set up correctly in sk_buffer, so it points to actual transport layer header (UDP/TCP). So you can use code like this:

udp_header = (struct udphdr *)skb_transport_header(skb);  

or better looking (but actually the same):

udp_header = udp_hdr(skb);  

Incoming packets (NF_INET_PRE_ROUTING)

This is the tricky part.

In this case the .transport_header field is not set to the actual transport layer header (UDP or TCP) in sk_buffer structure (that you get in your netfilter hook function). Instead, .transport_header points to IP header (which is network layer header).

So you need to calculate address of transport header by your own. To do so you need to skip IP header (i.e. add IP header length to your .transport_header address). That's why you can see next code in the article:

udp_header = (struct udphdr *)(skb_transport_header(skb) + 20);

So 20 here is just the length of IP header.

It can be done more elegant in this way:

struct iphdr *iph;
struct udphdr *udph;

iph = ip_hdr(skb);

/* If transport header is not set for this kernel version */
if (skb_transport_header(skb) == (unsigned char *)iph)
udph = (unsigned char *)iph + (iph->ihl * 4); /* skip IP header */
else
udph = udp_hdr(skb);

In this code we use an actual IP header size (which is iph->ihl * 4, in bytes) instead of magic number 20.

Another magic number in the article is 17 in next code:

if (ip_header->protocol == 17) {

In this code you should use IPPROTO_UDP instead of 17:

#include <linux/udp.h>

if (ip_header->protocol == IPPROTO_UDP) {

Netfilter input/output packets explanation

If you need some reference about difference between incoming and outgoing packets in netfilter, see the picture below.

netfilter-hooks

Details:

[1]: Some useful code from GitHub

[2]: "Linux Kernel Networking: Implementation and Theory" by Rami Rosen

[3]: This answer may be also useful

How to write a linux kernel module to modify source MAC address of a packet?

As its name suggests, NF_INET_POST_ROUTING is an INET layer hook. I think that you need NF_BR_POST_ROUTING which lives in netfilter_bridge.h (source):

/* Bridge Hooks */
/* After promisc drops, checksum checks. */
#define NF_BR_PRE_ROUTING 0
/* If the packet is destined for this box. */
#define NF_BR_LOCAL_IN 1
/* If the packet is destined for another interface. */
#define NF_BR_FORWARD 2
/* Packets coming from a local process. */
#define NF_BR_LOCAL_OUT 3
/* Packets about to hit the wire. */
#define NF_BR_POST_ROUTING 4

How to print correctly a IP using NETFILTER in C

Use %pI4 for the format specifier.

e.g. printk(KERN_DEBUG "IPs: %pI4 \t to \t %pI4 \n", &src_ip, &dest_ip);

This is documented in Documentation/printk-formats.txt.

Netfilter kernel module migrating to 4.1.23 - cannot retrieve ip header

Try extract this way:

struct iphdr *ip_hdr = (struct iphdr *)skb_network_header(skb);

and access this way:

printk("IP addres = %u  DEST = %u\n", ip_hdr->saddr, ip_hdr->daddr);


Related Topics



Leave a reply



Submit