【问题标题】:Print TCP Packet Data打印 TCP 数据包数据
【发布时间】:2015-06-15 17:35:57
【问题描述】:

在 TCP 通信中,当数据包从以太网传输到网络(IP)层时,我想打印该数据包中存在的数据?

我正在研究 linux。

我得到了一些信息,它可以在 linux 内核代码的帮助下完成,即在 linux NAT 防火墙代码中。但是我将在哪里获得内核源代码?这些编码是在哪里完成的?

【问题讨论】:

  • 什么 Linux 发行版?
  • wireshark 是另一种选择 ...
  • 我不想使用 wiresahrk。
  • 我想在 Linux 内核代码中搜索。我有一个 linux-3.13.0
  • @rbm 你不必使用 wireshark。但既然它完全按照你的意愿去做,你至少应该研究它,了解它是如何做到的,然后你就可以用同样的方式去做。

标签: c tcp linux-kernel hook netfilter


【解决方案1】:

如何打印 TCP 数据包中的数据

下面是一个完全符合您需要的示例:挂钩接收到的 TCP 数据包并打印其有效负载。如果您想从收到的数据包中打印一些其他信息(如二进制数据),您只需修改此注释下的部分:

/* ----- Print all needed information from received TCP packet ------ */

如果您需要跟踪 transmitted 数据包而不是 received 数据包,则可以替换此行:

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]。特别是您对下一章感兴趣:

  • 第 11 章:第 4 层协议
    • TCP(传输控制协议)
      • 使用 TCP 从网络层 (L3) 接收数据包
      • 使用 TCP 发送数据包
  • 第 9 章:网络过滤器
    • Netfilter 挂钩

如何获取Linux内核源代码

您可以使用您喜欢的一种方式获取内核源代码:

  1. 来自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
    
  2. 您的发行版中的内核源。例如。在 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 堆栈上,这样我就不需要处理丢包、重新排序等问题了吗?

Netfilter 钩子在ip_rcv() 函数(here)中被调用,所以你基本上是在 IPv4 层(OSI 中的网络层)工作。所以我相信在那个 netfilter 钩子中还没有处理数据包丢失处理、数据包重新排序等。

查看下一个链接以获取见解:

如果您想在传输层 (TCP) 上挂接数据包 - netfilter 不足以完成此任务,因为它仅在网络层 (IPv4) 中工作。

【讨论】:

  • 你好 Sam,我可以问一下这个例子的钩子在哪里捕获数据包吗?换句话说,它是否在 TCP 堆栈上,这样我就不需要处理丢包、重新排序等问题?谢谢!
  • @zzy 我已经在 UPDATE 部分回答了你的问题(在我上面的回答中)。
  • @AlirezaSanaee 听起来像是面试中的一个问题 :) 我不是内核网络方面的专家,所以我不能肯定地告诉你。但我可以假设是的,你可以做到这一点。确定最快的方法是进行某种测试,它应该相对容易。使用这个answer(尤其是图表)作为参考,提出代码来进行交换,然后检查它是否有效,例如与 Wireshark。一定要在这里提供你的反馈,以防你要做这样的实验,因为现在我也很好奇;)
  • @AlirezaSanaee 嗯,有趣的发现。我建议您在 SO 上创建一个新问题(并可能在此处链接该线程)。因为恐怕我无法帮助您解决这个问题,而不是我的能力,唉。
  • @zzy iptables 支持 nfqueue (netfilter queue) 以及第 4 层协议的过滤器,如 tcp、udp、icmp 等。如果需要(在大规模情况下,它会非常慢),您可以设置一个 iptables 过滤器并将截获的数据包从内核空间排队到用户空间套接字,从锁定到该队列的用户空间程序中修改/读取或做任何您想做的事情,然后给它一个判决(REJECT,DROP,ACCEPT) . ACCEPT 是唯一一个会发送它以被下一个钩子捕获或在此之后由内核处理的函数。查看 iptables nfqueue
【解决方案2】:

在内核级别不确定。

您可以使用libpcap 实用程序来捕获数据包并对其进行分析。比如这个:

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

【讨论】:

    【解决方案3】:

    您想使用tcpdump 工具检查网络上的 TCP 数据包。

    您没有说明您要查看的数据类型。

    This 会将流量转储到 DNS 的端口 53

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

    This page 有一个很好的概述。

    【讨论】:

    • 我想在 Linux 内核代码中搜索。我有一个 linux-3.13.0
    【解决方案4】:

    我不知道“从以太网传输到网络层”是什么意思。您的意思是当内核停止处理第 2 层标头并移动到第 3 层标头时?

    我将由您自行决定如何使用这些信息。如果要过滤和拦截第 3 层数据包(IP 数据包),Linux 上有两个主要选项。首先,您可以编写一个 netfilter 挂钩(为此您需要内核编程知识和一些 C 技能)。这基本上是一个内核模块,因此您必须自己编写第 4 层过滤器。 Linux 在它的库中有struct tcphdrstruct ip,只要谷歌,你就可以找到包含定义。

    其他选项,我不建议在您希望它执行良好的环境中使用 iptables 或 nftables 将数据包排队到用户空间。这更容易编程,因为您可以直接从 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 是,好吧 Python,但 scapy 有一些很酷的数据包​​操作的东西)。给您的提示:如果您以这种方式修改第 4 层数据包,则使用校验和会很痛苦。在我将它设置为零后,我无法让 netfilter 自动计算校验和,我建议在线为 IP 和 TCP 找到一个有效的校验和计算函数,因为你已经在编写一些不应该在生产中使用的东西。

    如果你想拦截第 2 层帧(以太网帧),我知道自内核 2.x 时代以来 Linux 中就有 ebtables。但是,这没有我所知道的简单的 NFQUEUE 类型的解决方案,因此您只能编写自己的代码。我相信它有用于创建过滤器和修改数据包的用户空间 API,所以你可能会很幸运。如果用户空间 API 不起作用,请享受编写内核模块的乐趣 :)。

    【讨论】:

      【解决方案5】:

      感谢@Sam Protsenko 的回答。但是对于 >= 4.13 的内核版本,函数 nf_register_hook(&amp;nfho)nf_unregister_hook(&amp;nfho) 已被替换为 nf_register_net_hook(&amp;init__net, &amp;nfho)nf_unregister_net_hook(&amp;init__net, &amp;nfho)

      如果您想尝试代码,请检查您的内核版本并根据您的情况修改代码。

      另外,对于初学者,你可能想要apt install sparse,这是Makefile中使用的内核代码错误检查器。

      【讨论】:

        【解决方案6】:

        你可以像这样使用 tcpdump:

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

        或者更好:

        tcpdump -i NameOfYourDevice -a -x -n port 80
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2015-02-02
          • 1970-01-01
          • 2012-12-02
          • 2016-01-08
          • 2016-11-28
          • 2012-12-21
          • 2013-11-25
          相关资源
          最近更新 更多