在TCP通信中,当数据包从以太网传输到网络(IP)层时,我想打印该数据包中的数据?
我正在使用linux。
我得到了一些信息,可以借助linux内核代码在linux NAT防火墙代码中完成。但是我会在哪里获得内核源代码?这些编码在哪里?
答案 0 :(得分:20)
下面是一个完全满足您需求的示例:挂钩接收TCP数据包并打印其有效负载。如果你想从收到的数据包中打印一些其他信息(比如二进制数据),你只需要修改一下这个评论下的部分:
/* ----- Print all needed information from received TCP packet ------ */
如果您需要跟踪传输的数据包而非已接收数据包,则可以替换此行:
nfho.hooknum = NF_INET_PRE_ROUTING;
这一个:
nfho.hooknum = NF_INET_POST_ROUTING;
保存下一个文件并发出make
命令来构建内核模块。然后执行sudo insmod print_tcp.ko
加载它。之后,您将能够使用dmesg
命令查看嗅探信息。如果要卸载模块,请运行sudo rmmod print_tcp
命令。
<强> 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");
<强>生成文件强>:
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
我建议你阅读这本书:[4]。特别是您对下一章感兴趣:
您可以使用您喜欢的方式获取内核源代码:
来自kernel.org的香草内核(更具体地来自 kernel/git/torvalds/linux.git),使用Git。例如。如果您需要k3.13,可以通过下一步方式完成:
$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
$ cd linux/
$ git checkout v3.13
来自发行版的内核源代码。例如。在Debian中,您只需安装linux-source
包(来源将安装到/usr/src
)。对于Ubuntu,请参阅these instructions。
详细说明:
[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
钩子捕获此示例的数据包?换句话说,它是否在TCP堆栈上,以便我不需要处理数据包丢失,重新排序等?
在ip_rcv()
函数(here)中调用Netfilter挂钩,因此您基本上在IPv4层(OSI中的网络层)中工作。所以我认为丢包处理,数据包重新排序等在netfilter挂钩中不处理。
请参阅下一个链接了解见解:
如果您想在传输层(TCP)上使用挂钩数据包 - netfilter不足以执行此任务,因为它仅适用于网络层(IPv4)。
答案 1 :(得分:0)
您希望使用tcpdump
工具检查线路上的TCP数据包。
您没有说出您要查看的数据类型。
This会在DNS端口53上转储流量
tcpdump -vvv -s 0 -l -n port 53
This page有一个很好的概述。
答案 2 :(得分:0)
答案 3 :(得分:0)
我不知道“从以太网传输到网络层”是什么意思。您的意思是当内核停止处理第2层标头并移至第3层标头时吗?
我将由您自行使用此信息。如果要过滤和拦截第3层数据包(IP数据包),在Linux上有两个主要选项。首先,您可以编写一个netfilter钩子(为此您需要内核编程知识和一些C技能)。这基本上是一个内核模块,因此您必须自己编写第4层过滤器。 Linux的库中有struct tcphdr
和struct ip
,只有google,您可以找到包含定义。
我不建议在您希望它表现良好的环境中使用的其他选项是使用iptables或nftable将数据包排队到用户空间。这很容易编程,因为您可以直接从cli使用IPtables和nftables挂钩,而不必担心学习内核模块编程。为此,示例iptables挂钩为iptables -A PREROUTING -p tcp --dport 8000 -j NFQUEUE --queue-num 0
。这会将在PREROUTING中捕获的,发往端口8000的所有tcp数据包都路由到netfilter队列号0(基本上只是一个用户空间套接字)。您将需要为发行版安装libnetfilter_queue
或等效的数据包,然后可以使用多种语言捕获和修改此过滤器捕获的数据包。我个人知道并用C,Python,Rust和Golang编写了这些脚本(尽管golang在速度方面有些不足,Python很好,但是,scapy有一些很棒的数据包处理方面的东西)。给您的提示:如果您以这种方式修改第4层数据包,则使用校验和很麻烦。将其设置为零后,我无法让netfilter自动计算校验和,我建议在线为IP和TCP找到一个有效的校验和计算功能,因为您已经在写一些永远不会在生产中使用的东西。
如果您想拦截第2层帧(以太网帧),我知道ebtables从内核2.x开始就存在于Linux中。但是,据我所知,这没有一个简单的NFQUEUE类型的解决方案,因此您不得不编写自己的代码。我相信它具有用于创建过滤器和修改数据包的用户空间API,因此您可能会很幸运。如果用户空间API不起作用,请编写内核模块:)。
答案 4 :(得分:0)
感谢@Sam Protsenko 的回答。但是对于内核版本 >= 4.13,函数 nf_register_hook(&nfho)
和 nf_unregister_hook(&nfho)
已被替换为 nf_register_net_hook(&init__net, &nfho)
和 nf_unregister_net_hook(&init__net, &nfho)
。
如果您想尝试代码,请检查您的内核版本并根据您的情况修改代码。
此外,对于初学者来说,您可能想要apt install sparse
,它是 Makefile 中使用的内核代码错误检查器。
答案 5 :(得分:-2)
您可以像这样使用tcpdump:
tcpdump -vvv -s 0 -l -n port 80 -i NameOfYourDevice
或更好:
tcpdump -i NameOfYourDevice -a -x -n port 80