Search code examples
ctcplinux-kernelhooknetfilter

Print TCP Packet Data


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?


Solution

  • 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).