5

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?

Sam Protsenko
  • 14,045
  • 4
  • 59
  • 75
rbm
  • 61
  • 1
  • 1
  • 4

6 Answers6

24

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

Community
  • 1
  • 1
Sam Protsenko
  • 14,045
  • 4
  • 59
  • 75
  • 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! – zzy May 31 '16 at 13:46
  • @zzy I've answered your question in **UPDATE** section (in my answer above). – Sam Protsenko May 31 '16 at 18:45
  • @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. – zzy Jun 01 '16 at 02:36
  • @zzy Let's cross-link your original question here: [link](http://stackoverflow.com/questions/37490968/where-to-find-the-tcp-data-under-http-inside-linux-kernel?noredirect=1#comment62560765_37490968). – Sam Protsenko Jun 01 '16 at 13:24
  • @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. – Sam Protsenko Jun 01 '16 at 14:05
  • @SamProtsenko Thanks! I've posted a new one here: http://stackoverflow.com/questions/37574383/how-to-intercept-packet-on-tcp-layer-in-kernel-to-analyze-data – zzy Jun 01 '16 at 16:43
  • 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?! – Alireza Sanaee Mar 08 '18 at 12:10
  • 1
    @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](https://stackoverflow.com/a/29663558/3866447) (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 ;) – Sam Protsenko Mar 08 '18 at 14:04
  • @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!!!!! – Alireza Sanaee Mar 08 '18 at 15:57
  • 1
    @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. – Sam Protsenko Mar 09 '18 at 01:30
  • 1
    @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 – rassa45 Jun 27 '19 at 03:30
0

You want to use the tcpdump tool to inspect TCP packets on the wire.

You didn't say what type of data you are trying to view.

This would dump traffic on port 53 for DNS

tcpdump -vvv -s 0 -l -n port 53

This page has a good overview.

Sam Protsenko
  • 14,045
  • 4
  • 59
  • 75
taco
  • 1,367
  • 17
  • 32
0

Not sure at the Kernel level.

You can use the libpcap utilities to capture a packet and dissect it. For example this:

http://yuba.stanford.edu/~casado/pcap/section2.html

Prabhu
  • 3,443
  • 15
  • 26
0

I have no clue what you mean by "transferred from ethernet to network layer". Do you mean when the kernel stops processing the layer 2 headers and moves onto the layer 3 headers?

I'm gonna leave it up to you to use this information as you will. If you want to filter and intercept layer 3 packets (IP packets), you have two main options on Linux. First, you can write a netfilter hook (for which you'll need kernel programming knowledge and some skill with C). This is basically a kernel module, so you'll have to write the layer 4 filters yourself. Linux has struct tcphdr and struct ip in it's libraries, just google and you can find the include definitions.

Other option, which I would not recommend in an environment where you want this to perform well, is to queue the packet to userspace using iptables or nftables. This is much easier to program with because you can use IPtables and nftables hooks directly from the cli and don't need to worry about learning kernel module programming. An example iptables hook for this is iptables -A PREROUTING -p tcp --dport 8000 -j NFQUEUE --queue-num 0. This will route any tcp packets captured in PREROUTING destined for port 8000 to the netfilter queue number 0 (which is basically just a userspace socket). You will need to install libnetfilter_queue or the equivalent packet for your distro, and then you can capture and modify packets caught by this filter in a variety of languages. I personally know and have written scripts for these in C, Python, Rust, and Golang (although golang was a bit bleh in terms of speed and Python is, well Python, but scapy has some cool packet manipulation stuff). Tip for you: if you are modifying layer 4 packets this way, checksums are a pain to work with. I couldn't get netfilter to calculate the checksums automatically after I set it to zero, I'd recommend finding a working checksum calculation function online for both IP and TCP because you're already writing something that should never be in production.

If you want to intercept Layer 2 frames (ethernet frames), I know that ebtables is there in Linux since the days of kernel 2.x. However, this does not have an easy NFQUEUE type solution that I know of, so you're stuck writing your own code. I believe it has userspace APIs for creating filters and modifying packets though so you may be in luck there. If the userspace API doesn't work, have fun writing a kernel module :).

rassa45
  • 3,482
  • 1
  • 29
  • 43
0

Thanks for @Sam Protsenko's answer. but for kernel version >= 4.13, the function nf_register_hook(&nfho) and nf_unregister_hook(&nfho) have been replaced to nf_register_net_hook(&init__net, &nfho) and nf_unregister_net_hook(&init__net, &nfho).

If you want to try the code, do check your kernel version and modify the code according to your situation.

Besides, for beginners, you may want to apt install sparse, which is a kernel code error checker used in the Makefile.

JackABlack
  • 11
  • 2
-2

You can use tcpdump like this:

tcpdump -vvv -s 0 -l -n port 80 -i NameOfYourDevice

Or better:

tcpdump -i NameOfYourDevice -a -x -n port 80
Sam Protsenko
  • 14,045
  • 4
  • 59
  • 75
Michael Popovich
  • 301
  • 1
  • 10