从 TPROXY 的流量中获取 UDP 目标 Address:Port

Getting UDP Destination Address:Port from TPROXY'd Traffic

我正在尝试从由 iptables TPROXY 规则重定向的 UDP 数据包中获取原始目的地。然而,有几种方法都失败了,使用原始套接字从 IP header 中获取数据包似乎是乱码。

混淆确实是双重的。后一个小包是SOCK_DGRAM得到的UDP payload,但是改成了SOCK_RAW returns这个乱码,没有payload的踪迹,也没有IP header。

错误的东西:

Port: 0 -- 45 00 00 48 1c ac 00 00 6e 11 7b f6 1b 46 8b 53 8b 63 82 06 69 7d 69 b4 00 34 29 5f 00 ba 03 00 47 20 01 00 30 2b ca 00 01 7d 8d 58 e8 89 88 0e 60 7f ee 00 c0 0b e3 01 00 00 00 00 00 00 00 06 0d e3 01 80 83 06 85 80 received 74 bytes

正确的负载:Port: 0 -- ff ff ff ff 55 4b a1 d5 77 received 9 bytes

未受 TPROXY 影响的正确原始数据包如下所示:

45-00-00-65-17-4B-40-00-3A-11-46-F8-2D-23-01-BA-4E-9F-64-C9-69-CD-26-8F-00-51-C2-C9-FF-FF-FF-FF-52-4C-20-30-36-2F.....

FF-FF-FF-FF-5x 是所需负载的开始,端口位于 69CD。

已移至 C/C++ 套接字代码,因为 C# 似乎没有 IP_TRANSPARENT TPROXY 工作显然需要的标志,我已尝试为 IP_TRANSPARENT, IP_RECVORIGSTDADDR, IP_HDRINCL.

我从 recvfrom 更改为 recvmsg 和 msghdrs,以便尝试循环遍历辅助数据以从 sockaddr_in 结果中获取 sin_port。我从不同的 SO 问题中拼凑了很多尝试,诚然,我真的不知道在这个级别的套接字代码中发生了什么,cmsghdr、cmsg、msghdr、iovecs 等对我来说都是新的。这里似乎没有任何作用,无论 TPROXY 对这些数据包做了什么,它们都无法使用。

这是抓取数据包、打印十六进制并尝试获取端口的内容(无论如何结果都是 0)

recvlen = recvmsg(fd, &message, 0);
printf("received %d bytes\n", recvlen);

for (cmsghdr *cmsg = CMSG_FIRSTHDR(&message); cmsg != NULL; cmsg = CMSG_NXTHDR(&message, cmsg))
    {
        if (cmsg->cmsg_level != SOL_IP || cmsg->cmsg_type != IP_ORIGDSTADDR) continue;
       printf("copying port\n");
        std::memcpy(&dstIpAddr, CMSG_DATA(cmsg), sizeof(sockaddr_in));
        dstPort = ntohs(dstIpAddr.sin_port);
    }

    printf("Port: %i -- ", dstPort);
    //std::cout << "Destination port: " << dstPort << std::endl;
    for (int i = 0; i < recvlen; i++)
    {
        printf("%02x ", buf[i]);
    }

iptables 规则将匹配十六进制字符串的数据包重定向到此套接字绑定到的端口:

TPROXY     udp  --  anywhere             anywhere             udp dpts:27000:28000 STRING match  "|ffffffff54|" ALGO name bm TO 65535 TPROXY redirect 0.0.0.0:29000 mark 0x1/0x1
TPROXY     udp  --  anywhere             anywhere             udp dpts:27000:28000 STRING match  "|ffffffff55|" ALGO name bm TO 65535 TPROXY redirect 0.0.0.0:29000 mark 0x1/0x1
TPROXY     udp  --  anywhere             anywhere             udp dpts:27000:28000 STRING match  "|ffffffff56|" ALGO name bm TO 65535 TPROXY redirect 0.0.0.0:29000 mark 0x1/0x1
TPROXY     udp  --  anywhere             anywhere             udp dpts:27000:28000 STRING match  "|ffffffff57|" ALGO name bm TO 65535 TPROXY redirect 0.0.0.0:29000 mark 0x1/0x1

我只需要获取目的地 address:port,以便转发流量以有条件地将其转发到它应该去的地方,并将结果发回。如果我可以使用 UDP 套接字执行此操作并以某种方式获取有效负载,然后从某些函数获取端口,fab。如果我必须获取原始数据包然后从 IP header 获取它,那也没关系,除非我不能,因为 TPROXY 似乎在混淆这些数据包。

相信 我通过 sizeof(buffer) 将 iov_len 设置为 const char* 的大小,因此基本上没有可写的 space。在摆弄声明并以此结束后,我使用 cmsg 循环正确获得了 IP 和端口。我仍然不知道为什么 TPROXY 在通过原始套接字查看数据包时会扭曲数据包,这很有趣。

iov.iov_base = cPacket;
iov.iov_len  = BUFSIZE;

mHeader.msg_name       = &sSourceAddr; //sockaddr_storage
mHeader.msg_namelen    = sizeof(struct sockaddr_in);
mHeader.msg_iov        = &iov;
mHeader.msg_iovlen     = 1;
mHeader.msg_control    = cControl;
mHeader.msg_controllen = BUFSIZE;

for (cmsghdr *cmsg = CMSG_FIRSTHDR(&mHeader); cmsg != NULL; cmsg = CMSG_NXTHDR(&mHeader, cmsg))
{
    if (cmsg->cmsg_level != SOL_IP || cmsg->cmsg_type != IP_ORIGDSTADDR) continue;
    std::memcpy(&sDestIP, CMSG_DATA(cmsg), sizeof(sockaddr_in));
    iPort = ntohs(sDestIP.sin_port);
    sAddress = sDestIP.sin_addr;
}