Altering packets on the fly with Netfilter QUEUE

Sebastien Tricaud <toady@gscore.org>


Who should read this doc ?

People who are curious about Netfilter and/or anyone who want to play dirty tricks at work, such as all people who use networks such as MSN and want to replace the word "love" by "hate". Then conversations will be :
Hi honey!
Hello sweatheart
I hate you!Note: He wrote here "I love you!"
What ?
Let me assure you, I insist on this word: I hate you!
I sleep with my sister tonight! I am shocked by your
words. I thought things were clear enough between
you and me! Good luck with your nerdy life!
Simone left

What is Netfilter QUEUE ?

QUEUE is the name for the netfilter target that requires a userspace application for the packet verdict. This application is registered to the nflink socket. It is a really powerful way to extend a firewall such as what INL did with the NuFW project in order to have a real authenticating firewall.

Linux kernel textsearch API

The Linux textsearch API is documented in the source file lib/textsearch.c. It offers the possibility to performs efficient linear or non-linear string matching with several algorithms.
To perform quick search in payload patterns that will be replaced further, we can use several algorithm. On my state-of-the-art ubuntu desktop I have Boyer & Moore (ts_bm), naive finite state machine (ts_fsm) and Knuth-Morris-Pratt (ts_kmp) algorithms available as separate modules. This is a short description of what each are :

Our packet helper library

In order to perform simple packet manipulation and understand which layer we are dealing with, I wrote a very simple set of functions to write/get packet informations : The packet structure:
/* very basic structure for accessing data from a packet */
typedef struct packet_accessor_t {

        struct iphdr *ip;
        struct tcphdr *tcp;
        struct udphdr *udp;

        /* raw payload */
        unsigned char *payload;

        /* User data or function */
        void *user;

} packet_accessor_t;
And three functions that deal with it:
packet_accessor_t *packet_new(void);
int packet_feed(packet_accessor_t *pkt, unsigned char *capture_data);
void packet_free(packet_accessor_t *pkt);
Where packet_new will create a packet accessor instance, packet_feed fill the structure from captured data, packet_replace will replace a buffer at a given position by an other and recalculate the payload size and packet_free clean the mess up.

Writing the code

So now we know what we want to do and how :
  1. Grab packet data using Netfilter API
  2. Feed those data into our packet helper library
  3. Use the linux kernel text search API to perform pattern matching
  4. Replace the old data by the new
  5. Reset the packet ip and udp checksum to 0 to let the operating system recalculate latter
  6. Have a beer


1. Grab packet data using Netfilter API

ret = nfq_get_payload(nfa, &payload);

2. Feed those data into our packet helper library

pkt = packet_new();
packet_feed(pkt, payload);

3. Use the linux kernel text search API to perform pattern matching

int pos;
struct ts_config *conf;
struct ts_state state;
const char *pattern = "chicken";
const char *example = "We dance the funky chicken";

conf = textsearch_prepare("kmp", pattern, strlen(pattern),
                          GFP_KERNEL, TS_AUTOLOAD);

if (IS_ERR(conf)) {
        err = PTR_ERR(conf);
        goto errout;
}

pos = textsearch_find_continuous(conf, &state, example, strlen(example));                                              
if (pos != UINT_MAX)
        panic("Oh my god, dancing chickens at %d\n", pos);                                                                  
textsearch_destroy(conf);   

4. Replace the old data by the new

Use the packet_replace function

5. Reset the packet ip and udp checksum to 0 to let the operating system recalculate latter

pkt->ip->check = 0;
pkt->udp->check = 0;

6. Have a beer