5
votes

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?

6
What Linux distribution?taco
wireshark is another option ...rabi shaw
I didn't want to use wiresahrk.rbm
I want to search in Linux kernel code. I have one linux-3.13.0rbm
@rbm You don't have to use wireshark. But since it does exactly what you want to do, you should at least study it, learn how it does it, and then you can do it the same way.David Schwartz

6 Answers

24
votes

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

0
votes

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

0
votes

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.

0
votes

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

0
votes

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.

-2
votes

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