Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
2.1k views
in Technique[技术] by (71.8m points)

linux - netfilter-like kernel module to get source and destination address

I read this guide to write a kernel module to do simple network filtering.

First, I have no idea of what below text this means, and what's the difference between inbound and outbound data packet(by transportation layer)?

When a packet goes in from wire, it travels from physical layer, data link layer, network layer upwards, therefore it might not go through the functions defined in netfilter for skb_transport_header to work.

Second, I hate magic numbers, and I want to replace the 20 (the length of typical IP header) with any function from the linux kernel's utilities(source file).

Any help will be appreciated.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...