Print TCP Packet Data

13,343

How to print data from TCP packets

Below is an example which does exactly what you need: hook received TCP packets and print their payloads. If you want to print some other information from received packet (like binary data), you just need to modify a bit the section under this comment:

/* ----- Print all needed information from received TCP packet ------ */

If you need to trace transmitted packets instead of received ones, you can replace this line:

nfho.hooknum = NF_INET_PRE_ROUTING;

with this one:

nfho.hooknum = NF_INET_POST_ROUTING;

Save next files and issue make command to build kernel module. Then do sudo insmod print_tcp.ko to load it. After that you will be able to see sniffed information using dmesg command. If you want to unload your module, run sudo rmmod print_tcp command.

print_tcp.c:

#include <linux/module.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/ip.h>
#include <linux/tcp.h>

#define PTCP_WATCH_PORT     80  /* HTTP port */

static struct nf_hook_ops nfho;

static unsigned int ptcp_hook_func(const struct nf_hook_ops *ops,
                                   struct sk_buff *skb,
                                   const struct net_device *in,
                                   const struct net_device *out,
                                   int (*okfn)(struct sk_buff *))
{
    struct iphdr *iph;          /* IPv4 header */
    struct tcphdr *tcph;        /* TCP header */
    u16 sport, dport;           /* Source and destination ports */
    u32 saddr, daddr;           /* Source and destination addresses */
    unsigned char *user_data;   /* TCP data begin pointer */
    unsigned char *tail;        /* TCP data end pointer */
    unsigned char *it;          /* TCP data iterator */

    /* Network packet is empty, seems like some problem occurred. Skip it */
    if (!skb)
        return NF_ACCEPT;

    iph = ip_hdr(skb);          /* get IP header */

    /* Skip if it's not TCP packet */
    if (iph->protocol != IPPROTO_TCP)
        return NF_ACCEPT;

    tcph = tcp_hdr(skb);        /* get TCP header */

    /* Convert network endianness to host endiannes */
    saddr = ntohl(iph->saddr);
    daddr = ntohl(iph->daddr);
    sport = ntohs(tcph->source);
    dport = ntohs(tcph->dest);

    /* Watch only port of interest */
    if (sport != PTCP_WATCH_PORT)
        return NF_ACCEPT;

    /* Calculate pointers for begin and end of TCP packet data */
    user_data = (unsigned char *)((unsigned char *)tcph + (tcph->doff * 4));
    tail = skb_tail_pointer(skb);

    /* ----- Print all needed information from received TCP packet ------ */

    /* Show only HTTP packets */
    if (user_data[0] != 'H' || user_data[1] != 'T' || user_data[2] != 'T' ||
            user_data[3] != 'P') {
        return NF_ACCEPT;
    }

    /* Print packet route */
    pr_debug("print_tcp: %pI4h:%d -> %pI4h:%d\n", &saddr, sport,
                              &daddr, dport);

    /* Print TCP packet data (payload) */
    pr_debug("print_tcp: data:\n");
    for (it = user_data; it != tail; ++it) {
        char c = *(char *)it;

        if (c == '\0')
            break;

        printk("%c", c);
    }
    printk("\n\n");

    return NF_ACCEPT;
}

static int __init ptcp_init(void)
{
    int res;

    nfho.hook = (nf_hookfn *)ptcp_hook_func;    /* hook function */
    nfho.hooknum = NF_INET_PRE_ROUTING;         /* received packets */
    nfho.pf = PF_INET;                          /* IPv4 */
    nfho.priority = NF_IP_PRI_FIRST;            /* max hook priority */

    res = nf_register_hook(&nfho);
    if (res < 0) {
        pr_err("print_tcp: error in nf_register_hook()\n");
        return res;
    }

    pr_debug("print_tcp: loaded\n");
    return 0;
}

static void __exit ptcp_exit(void)
{
    nf_unregister_hook(&nfho);
    pr_debug("print_tcp: unloaded\n");
}

module_init(ptcp_init);
module_exit(ptcp_exit);

MODULE_AUTHOR("Sam Protsenko");
MODULE_DESCRIPTION("Module for printing TCP packet data");
MODULE_LICENSE("GPL");

Makefile:

ifeq ($(KERNELRELEASE),)

KERNELDIR ?= /lib/modules/$(shell uname -r)/build

module:
    $(MAKE) -C $(KERNELDIR) M=$(PWD) C=1 modules

clean:
    $(MAKE) -C $(KERNELDIR) M=$(PWD) C=1 clean

.PHONY: module clean

else

MODULE = print_tcp.o
CFLAGS_$(MODULE) := -DDEBUG
obj-m := $(MODULE)

endif

Explanation

I would recommend you to read this book: [4]. Particularly you are interested in next chapters:

  • chapter 11: Layer 4 Protocols
    • TCP (Transmission Control Protocol)
      • Receiving Packets from the Network Layer (L3) with TCP
      • Sending Packets with TCP
  • chapter 9: Netfilter
    • Netfilter Hooks

How to obtain Linux kernel source code

You can obtain kernel source code using one of ways you prefer:

  1. Vanilla kernel from kernel.org (more specifically from kernel/git/torvalds/linux.git), using Git. E.g. if you need k3.13, it can be done next way:

    $ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
    $ cd linux/
    $ git checkout v3.13
    
  2. Kernel sources from your distro. E.g. in Debian you can just install linux-source package (sources will be installed to /usr/src). For Ubuntu see these instructions.


Details:

[1] How to get TCP header from sk_buff

[2] Network flow control in Linux kernel

[3] Writing Loadable Kernel Modules using netfilter hooks

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

[5] How to access data/payload from tcphdr


UPDATE

where the hook captures packets for this example? In other words, is it upon TCP stack so that I don't need to take care of packet losing, reordering, etc.?

Netfilter hook is called in ip_rcv() function (here), so you are basically working in IPv4 layer (which is Network layer in OSI). So I believe packet loss handling, packet reordering etc. is not handled yet in that netfilter hook.

See next links for insights:

If you want a hook packets upon Transport layer (TCP) -- netfilter is not sufficient for this task, as it works exclusively in Network layer (IPv4).

Share:
13,343
Admin
Author by

Admin

Updated on June 04, 2022

Comments

  • Admin
    Admin almost 2 years

    In TCP Communication, when the packet is being transferred from ethernet to network(IP) layer, I want to print the data present in that packet?

    I am working on linux.

    I got some information that it can be done with the help of linux kernel code i.e in linux NAT Firewall code. But where I will get kernel source code? Where these coding is being done?

  • Admin
    Admin about 9 years
    I want to search in Linux kernel code. I have one linux-3.13.0
  • zzy
    zzy almost 8 years
    Hello Sam, may I ask where the hook captures packets for this example? In other words, is it upon TCP stack so that I don't need to take care of packet losing, reordering, etc.? Thanks!
  • Sam Protsenko
    Sam Protsenko almost 8 years
    @zzy I've answered your question in UPDATE section (in my answer above).
  • zzy
    zzy almost 8 years
    @SamProtsenko Thank you so much! I've heard that there are several hooks inside kernel (please correct me if I'm wrong here), do you know does any of them locate upon TCP layer (so that reordering is not an issue any more)? It may be not wise to build another wheel to reorder the packets in the module if the data separated among multiple packets.
  • Sam Protsenko
    Sam Protsenko almost 8 years
    @zzy Let's cross-link your original question here: link.
  • Sam Protsenko
    Sam Protsenko almost 8 years
    @zzy Netfilter hook won't cut it, as I understand. Unfortunately, I don't know elegant way to do that. You should really ask the new question, something like "how to intercept packet on TCP layer in kernel to analyze data and then drop it or manually send it to some application", but with more details on it. You can mention why netfilter hook is not an option and also cross-link this question and your original question.
  • zzy
    zzy almost 8 years
    @SamProtsenko Thanks! I've posted a new one here: stackoverflow.com/questions/37574383/…
  • Alireza Sanaee
    Alireza Sanaee about 6 years
    Is it possible to swap the source and destination of ip header and also port of tcp header somewhere in the code and return NF_ACCEPT and expect the packet to go back to the originator?!
  • Sam Protsenko
    Sam Protsenko about 6 years
    @AlirezaSanaee Sounds like a question at the interview :) I'm not an expert in kernel networking, so I can't tell you for sure. But I can presume that yes, you can do that. The fastest way to know for sure, is to conduct some kind of test, it should be relatively easy. Use this answer (especially the diagram) as a reference, come up with code to do the swapping, then check if it works, e.g. with Wireshark. Be sure to provide your feedback here in case you're going to do such an experiment, because now I'm curious too ;)
  • Alireza Sanaee
    Alireza Sanaee about 6 years
    @SamProtsenko According to the diagram, it looks it is possible but I just ran a UDP server and send some UDP packets from another node, and write the proper kernel module. I swap the IPs and Ports in the proper headers but it doesn't forward the message, I even activate packet forwarding in the sysctl.config but no progress, I don't know if it is related to checksums or not. I think it is not since I changed the packet content(data) in kernel module then everything works well. It's weired since it should work :) but it is not!!!!!
  • Sam Protsenko
    Sam Protsenko about 6 years
    @AlirezaSanaee Hmm, interesting findings. I would suggest you to create a new question on SO (and probably link this thread there). Because I'm afraid I can't help you with this one, not my competence, alas.
  • rassa45
    rassa45 almost 5 years
    @zzy iptables supports nfqueue (netfilter queue) as well as filters for layer 4 protocols like tcp, udp, icmp, etc. If you want (in large scale situation it will be slow as hell), you can setup an iptables filter and queue intercepted packets from kernel space to a userspace socket, modify/read or do whatever you want with it from a userspace program that's latched onto that queue, then give it a verdict (REJECT, DROP, ACCEPT). ACCEPT is the only one that will send it through to be caught by the next hook or processed by the kernel after that. Look into iptables nfqueue