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 cleanelseMODULE = 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
- Receiving Packets from the Network Layer (L3) with TCP
- Sending Packets with TCP
- TCP (Transmission Control Protocol)
- chapter 9: Netfilter
- Netfilter Hooks
How to obtain Linux kernel source code
You can obtain kernel source code using one of ways you prefer:
- 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
- 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:
- Netfilter packet flow
- Control flow in Linux networking
- Network data flow through kernel (diagram)
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).