如何从多个 IP 数据包重建 TCP 流?

Posted

技术标签:

【中文标题】如何从多个 IP 数据包重建 TCP 流?【英文标题】:How to reconstruct TCP stream from multiple IP packets? 【发布时间】:2013-09-13 13:15:10 【问题描述】:

我正在开发一个基于 TUN 的 *** 服务器,其目标是在将接收到的数据包转发到目的地之前对其进行分析。目前,我正在从 TUN 接口接收 IP 数据包,并简单地将它们发送到未修改的目的地。

我知道分析 UDP 数据包的内容就像剥离 IP 和 UDP 标头一样简单。但是,为了分析 TCP 流量的内容,我需要从多个 IP 数据包中重建消息。有没有一种简单的方法可以在不重新实现 TCP 的情况下做到这一点?是否有任何用于此任务的易于访问的 C/C++ 库?我更喜欢 Linux 系统库和/或开源、非病毒/非 copyleft 库。

我已经考虑过的一件事是制作每个 IP 数据包的副本,并将副本的目标 IP 更改为 localhost,以便我的服务器的不同部分可以接收这些 TCP 请求和完全重构且没有标头的响应。但是,我无法将目标 IP 与流量内容相关联,这是我想要的。

【问题讨论】:

你不能把底层的东西留给你的内核并以合适的方式使用iptables吗?这完全有能力复制数据包和调用用户空间程序等等。 我希望程序最终控制数据包的去向和/或在转发之前修改其内容。 @Jomasi 你想修改数据包的去向?那是什么意思?对于 TCP 连接,它们应该转到连接的远程端。还有什么意义吗? 【参考方案1】:

您需要的功能可能总是与数据包解析紧密结合。确实需要好的协议解析器来提取所需的信息。所以我的建议是使用最好的开源工具 - wireshark.org

它提供“Follow TCP stream”功能:

我看起来不像你可以轻松提取部分 Wireshark 解剖逻辑,但至少有一个很好的例子packet-tcp:

typedef struct _tcp_flow_t 
    guint32 base_seq;   /* base seq number (used by relative sequence numbers)
                 * or 0 if not yet known.
                 */
    tcp_unacked_t *segments;
    guint32 fin;        /* frame number of the final FIN */
    guint32 lastack;    /* last seen ack */
    nstime_t lastacktime;   /* Time of the last ack packet */
    guint32 lastnondupack;  /* frame number of last seen non dupack */
    guint32 dupacknum;  /* dupack number */
    guint32 nextseq;    /* highest seen nextseq */
    guint32 maxseqtobeacked;/* highest seen continuous seq number (without hole in the stream) from the fwd party,
                 * this is the maximum seq number that can be acked by the rev party in normal case.
                 * If the rev party sends an ACK beyond this seq number it indicates TCP_A_ACK_LOST_PACKET contition */
    guint32 nextseqframe;   /* frame number for segment with highest
                 * sequence number
                 */

基本上有单独的对话提取逻辑,请注意find_conversationusage:

/* Attach process info to a flow */
/* XXX - We depend on the TCP dissector finding the conversation first */
void
add_tcp_process_info(guint32 frame_num, address *local_addr, address *remote_addr, guint16 local_port, guint16 remote_port, guint32 uid, guint32 pid, gchar *username, gchar *command) 
    conversation_t *conv;
    struct tcp_analysis *tcpd;
    tcp_flow_t *flow = NULL;

    conv = find_conversation(frame_num, local_addr, remote_addr, PT_TCP, local_port, remote_port, 0);
    if (!conv) 
        return;
    

实际逻辑有据可查,可用here:

/*
 * Given two address/port pairs for a packet, search for a conversation
 * containing packets between those address/port pairs.  Returns NULL if
 * not found.
 *
 * We try to find the most exact match that we can, and then proceed to
 * try wildcard matches on the "addr_b" and/or "port_b" argument if a more
 * exact match failed.
 * ...
 */
conversation_t *
find_conversation(const guint32 frame_num, const address *addr_a, const address *addr_b, const port_type ptype,
    const guint32 port_a, const guint32 port_b, const guint options)

   conversation_t *conversation;

   /*
    * First try an exact match, if we have two addresses and ports.
    */
   if (!(options & (NO_ADDR_B|NO_PORT_B))) 

所以我实际上建议的是使用EPAN library。可以提取此库并独立使用它。请谨慎使用许可证。

【讨论】:

【参考方案2】:

也许您可能对libipq - iptables 用户空间数据包队列库感兴趣。

#include <linux/netfilter.h>
#include <libipq.h>

Netfilter 提供了一种将数据包从堆栈中传递出去的机制 用于排队到用户空间,然后将这些数据包接收回 内核带有一个裁决,指定如何处理数据包(例如 接受或放弃)。这些数据包也可以在用户空间之前被修改 重新注入内核。对于每个支持的协议,一个 称为队列处理程序的内核模块可以向 Netfilter 注册以 执行将数据包传入和传出用户空间的机制。

IPv4 的标准队列处理程序是 ip_queue。它被提供为 具有 2.4 内核的实验模块,并使用 Netlink 套接字 内核/用户空间通信。

加载 ip_queue 后,可以使用 iptables 和 通过 QUEUE 目标排队等待用户空间处理

这里是如何分解 tcp/ip 数据包的简单示例:

ipq_packet_msg_t *m = ipq_get_packet(buf);

struct iphdr *ip = (struct iphdr*) m->payload;

struct tcphdr *tcp = (struct tcphdr*) (m->payload + (4 * ip->ihl));

int port = htons(tcp->dest);        

status = ipq_set_verdict(h, m->packet_id,
                          NF_ACCEPT, 0, NULL);
if (status < 0)
        die(h);

快intro

如果这不是您要查找的内容,您可以尝试使用wireshark EPAN library。

【讨论】:

这个库似乎没有提供任何方法来重建 TCP 数据包并将重建与其 IP 标头相关联。如果我错了,请纠正我。 请仔细看看。例如: ipq_set_mode(3) 设置队列模式,将数据包元数据或有效负载以及元数据复制到用户空间。 我发现 libipq 已被弃用,取而代之的是 nfnetlink_queue,它没有太多文档。无论哪种方式,在 IP 和 TCP 标头之后的 m->payload 中是否包含从可能的多个 IP 数据包重构的完整 TCP 有效负载?例如,通过 HTTP 传输的大型图像文件。 @computer 似乎一次只对单个 IP 数据包进行操作,并允许访问特定 TCP IP 数据包的有效负载。这似乎没有结合多个数据包的有效负载来构建整个 TCP 消息体。我弄错了吗? 如果这不是您想要的,您可以尝试使用wireshark EPAN library。

以上是关于如何从多个 IP 数据包重建 TCP 流?的主要内容,如果未能解决你的问题,请参考以下文章

SDN第三次作业

SDN第三次作业

如何确定原始数据包中IP头和TCP头的起始字节

tcp/ip协议能不能连接二个不同的网络?

TCP粘包/拆包问题

使用 libpcap,有没有办法从离线 pcap 文件中确定捕获的数据包的文件偏移量?